├── .clang-format ├── .clang-tidy ├── .gitignore ├── CMakeLists.txt ├── CPPLINT.cfg ├── LICENSE ├── README.md ├── presubmit.sh └── src ├── CMakeLists.txt ├── starkware ├── CMakeLists.txt ├── algebra │ ├── CMakeLists.txt │ ├── big_int.h │ ├── big_int.inl │ ├── big_int_test.cc │ ├── elliptic_curve.h │ ├── elliptic_curve.inl │ ├── elliptic_curve_test.cc │ ├── fraction_field_element.h │ ├── fraction_field_element.inl │ ├── fraction_field_element_test.cc │ ├── prime_field_element.cc │ ├── prime_field_element.h │ └── prime_field_element_test.cc ├── crypto │ ├── CMakeLists.txt │ ├── ecdsa.cc │ ├── ecdsa.h │ ├── ecdsa_test.cc │ ├── elliptic_curve_constants.cc │ ├── elliptic_curve_constants.h │ ├── elliptic_curve_constants_test.cc │ ├── ffi │ │ ├── CMakeLists.txt │ │ ├── crypto_lib │ │ │ └── crypto_lib.go │ │ ├── crypto_lib_test.go │ │ ├── ecdsa.cc │ │ ├── ecdsa.h │ │ ├── js │ │ │ ├── CMakeLists.txt │ │ │ ├── crypto.js │ │ │ ├── package.json │ │ │ └── test │ │ │ │ ├── crypto_test.js │ │ │ │ └── signature_test_data.json │ │ ├── pedersen_hash.cc │ │ ├── pedersen_hash.h │ │ ├── portable_endian.h │ │ ├── utils.cc │ │ └── utils.h │ ├── pedersen_hash.cc │ ├── pedersen_hash.h │ └── pedersen_hash_test.cc ├── starkex │ ├── CMakeLists.txt │ ├── order.cc │ ├── order.h │ └── order_test.cc └── utils │ ├── CMakeLists.txt │ ├── error_handling.h │ ├── math.h │ ├── math_test.cc │ ├── prng.h │ ├── prng_test.cc │ └── test_utils.h └── third_party └── gsl ├── LICENSE └── gsl-lite.hpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Google 3 | AlignAfterOpenBracket: AlwaysBreak 4 | ColumnLimit: 100 5 | DerivePointerAlignment: false 6 | IncludeBlocks: Preserve 7 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: '*,-fuchsia-*,-llvm-header-guard,-hicpp-*,-cert-err58-cpp,-modernize-use-auto,-modernize-use-equals-delete,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-special-member-functions' 3 | WarningsAsErrors: '' 4 | AnalyzeTemporaryDtors: false 5 | FormatStyle: none 6 | CheckOptions: 7 | - {key: readability-identifier-naming.AbstractClassCase, value: CamelCase} 8 | - {key: readability-identifier-naming.ClassCase, value: CamelCase} 9 | - {key: readability-identifier-naming.ClassMethodCase, value: CamelCase} 10 | - {key: readability-identifier-naming.ConstexprFunctionCase, value: CamelCase} 11 | - {key: readability-identifier-naming.ConstexprMethodCase, value: CamelCase} 12 | - {key: readability-identifier-naming.ConstexprVariableCase, value: CamelCase} 13 | - {key: readability-identifier-naming.ConstexprVariablePrefix, value: 'k'} 14 | - {key: readability-identifier-naming.EnumConstantPrefix, value: 'k'} 15 | - {key: readability-identifier-naming.EnumCase, value: CamelCase} 16 | - {key: readability-identifier-naming.EnumConstantCase, value: CamelCase} 17 | - {key: readability-identifier-naming.FunctionCase, value: CamelCase} 18 | - {key: readability-identifier-naming.GlobalConstantCase, value: CamelCase} 19 | - {key: readability-identifier-naming.GlobalConstantPrefix, value: 'k'} 20 | - {key: readability-identifier-naming.GlobalFunctionCase, value: CamelCase} 21 | - {key: readability-identifier-naming.GlobalVariableCase, value: lower_case} 22 | - {key: readability-identifier-naming.InlineNamespaceCase, value: lower_case} 23 | - {key: readability-identifier-naming.LocalConstantCase, value: lower_case} 24 | - {key: readability-identifier-naming.LocalVariableCase, value: lower_case} 25 | - {key: readability-identifier-naming.MemberCase, value: lower_case} 26 | - {key: readability-identifier-naming.MemberSuffix, value: '_'} 27 | - {key: readability-identifier-naming.PublicMemberCase, value: lower_case} 28 | - {key: readability-identifier-naming.MethodCase, value: CamelCase} 29 | - {key: readability-identifier-naming.NamespaceCase, value: lower_case} 30 | - {key: readability-identifier-naming.ParameterCase, value: lower_case} 31 | - {key: readability-identifier-naming.ConstantParameterCase, value: lower_case} 32 | - {key: readability-identifier-naming.ParameterPackCase, value: lower_case} 33 | - {key: readability-identifier-naming.PureFunctionCase, value: lower_case} 34 | - {key: readability-identifier-naming.PureMethodCase, value: CamelCase} 35 | - {key: readability-identifier-naming.StaticConstantCase, value: CamelCase} 36 | - {key: readability-identifier-naming.StaticConstantPrefix, value: 'k'} 37 | - {key: readability-identifier-naming.StaticVariableCase, value: lower_case} 38 | - {key: readability-identifier-naming.StructCase, value: CamelCase} 39 | - {key: readability-identifier-naming.TemplateParameterCase, value: CamelCase} 40 | - {key: readability-identifier-naming.TemplateTemplateParameterCase, value: CamelCase} 41 | - {key: readability-identifier-naming.TemplateUsingCase, value: lower_case} 42 | - {key: readability-identifier-naming.TypeTemplateParameterCase, value: CamelCase} 43 | - {key: readability-identifier-naming.TypedefCase, value: lower_case} 44 | - {key: readability-identifier-naming.UnionCase, value: CamelCase} 45 | - {key: readability-identifier-naming.UsingCase, value: lower_case} 46 | - {key: readability-identifier-naming.ValueTemplateParameterCase, value: CamelCase} 47 | - {key: readability-identifier-naming.VariableCase, value: lower_case} 48 | - {key: readability-identifier-naming.VirtualMethodCase, value: CamelCase} 49 | - {key: readability-identifier-naming.IgnoreFailedSplit, value: 0} 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /.vscode/ 3 | node_modules 4 | *.so 5 | package-lock.json 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.5) 2 | if (NOT DEFINED CMAKE_CXX_COMPILER) 3 | set(CMAKE_CXX_COMPILER /usr/bin/clang++-6.0) 4 | endif() 5 | 6 | project(StarkwareCryptoLib VERSION 0.1.0 LANGUAGES CXX) 7 | include(CTest) 8 | # Basic setup for gtest 9 | find_package(GTest REQUIRED) 10 | include_directories(${GTEST_INCLUDE_DIRS}) 11 | enable_testing() 12 | include_directories("${CMAKE_SOURCE_DIR}/src") 13 | 14 | if (NOT DEFINED CMAKE_BUILD_TYPE) 15 | set(CMAKE_BUILD_TYPE Debug) 16 | endif() 17 | 18 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 19 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -Werror -Wall -Wextra -fno-strict-aliasing -fPIC") 20 | 21 | set(CC_OPTIMIZE "-O3") 22 | 23 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g") 24 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${CC_OPTIMIZE}") 25 | 26 | add_subdirectory(src) 27 | -------------------------------------------------------------------------------- /CPPLINT.cfg: -------------------------------------------------------------------------------- 1 | filter=-legal/copyright,-build/c++11 2 | linelength=100 3 | root=src 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2020 StarkWare Industries Ltd. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repo requires the installation of the Golang compiler on the system in order to run the 2 | presubmit script and the provided Go FFI and test. To install Go under Ubuntu, run the following: 3 | 4 | ``sudo apt install golang-go`` 5 | -------------------------------------------------------------------------------- /presubmit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # install npm packages. 6 | (cd src/starkware/crypto/ffi/js; npm install) 7 | 8 | # Compile the code. 9 | mkdir -p build/Release 10 | (cd build/Release; cmake -DCMAKE_BUILD_TYPE=Release ../..) 11 | make -C build/Release 12 | 13 | # Run tests. 14 | CTEST_OUTPUT_ON_FAILURE=1 make -C build/Release test 15 | 16 | # Run go testing (we assume golang-go is installed). 17 | go test build/Release/src/starkware/crypto/ffi/crypto_lib_test.go 18 | 19 | clang-tidy-6.0 -header-filter=src/starkware -p=build/Release $(find src/starkware -name "*.cc" | grep -v node_modules) 20 | cpplint --extensions=cc,h $(find src/starkware | grep -v node_modules | grep -E '\.(cc|h)$') 21 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(starkware) 2 | -------------------------------------------------------------------------------- /src/starkware/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(algebra) 2 | add_subdirectory(utils) 3 | add_subdirectory(crypto) 4 | add_subdirectory(starkex) 5 | -------------------------------------------------------------------------------- /src/starkware/algebra/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(algebra prime_field_element.cc) 2 | 3 | add_executable(big_int_test big_int_test.cc) 4 | target_link_libraries(big_int_test gtest gtest_main pthread) 5 | add_test(big_int_test big_int_test) 6 | 7 | add_executable(prime_field_element_test prime_field_element_test.cc) 8 | target_link_libraries(prime_field_element_test algebra gtest gtest_main pthread) 9 | add_test(prime_field_element_test prime_field_element_test) 10 | 11 | add_executable(fraction_field_element_test fraction_field_element_test.cc) 12 | target_link_libraries(fraction_field_element_test algebra gtest gtest_main pthread) 13 | add_test(fraction_field_element_test fraction_field_element_test) 14 | 15 | add_executable(elliptic_curve_test elliptic_curve_test.cc) 16 | target_link_libraries(elliptic_curve_test algebra gtest gtest_main pthread) 17 | add_test(elliptic_curve_test elliptic_curve_test) 18 | -------------------------------------------------------------------------------- /src/starkware/algebra/big_int.h: -------------------------------------------------------------------------------- 1 | #ifndef STARKWARE_ALGEBRA_BIG_INT_H_ 2 | #define STARKWARE_ALGEBRA_BIG_INT_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "third_party/gsl/gsl-lite.hpp" 12 | 13 | #include "starkware/utils/error_handling.h" 14 | #include "starkware/utils/prng.h" 15 | 16 | namespace starkware { 17 | 18 | static constexpr inline __uint128_t Umul128(uint64_t x, uint64_t y) { 19 | return static_cast<__uint128_t>(x) * static_cast<__uint128_t>(y); 20 | } 21 | 22 | template 23 | class BigInt { 24 | public: 25 | static constexpr size_t kDigits = N * std::numeric_limits::digits; 26 | 27 | BigInt() = default; 28 | 29 | template 30 | constexpr BigInt(const BigInt& v) noexcept; // NOLINT implicit cast. 31 | constexpr explicit BigInt(const std::array& v) noexcept : value_(v) {} 32 | constexpr explicit BigInt(uint64_t v) noexcept : value_(std::array({v})) {} 33 | 34 | static constexpr BigInt One() { return BigInt(std::array({1})); } 35 | static constexpr BigInt Zero() { return BigInt(std::array({0})); } 36 | 37 | static BigInt RandomBigInt(Prng* prng); 38 | 39 | /* 40 | Returns pair of the form (result, overflow_occurred). 41 | */ 42 | static constexpr std::pair Add(const BigInt& a, const BigInt& b); 43 | constexpr BigInt operator+(const BigInt& other) const { return Add(*this, other).first; } 44 | constexpr BigInt operator-(const BigInt& other) const { return Sub(*this, other).first; } 45 | constexpr BigInt operator-() const { return Zero() - *this; } 46 | 47 | /* 48 | Multiplies two BigInt numbers, this and other. Returns the result as a 49 | BigInt<2*N>. 50 | */ 51 | constexpr BigInt<2 * N> operator*(const BigInt& other) const; 52 | 53 | /* 54 | Multiplies two BigInt numbers modulo a third. 55 | */ 56 | static BigInt MulMod(const BigInt& a, const BigInt& b, const BigInt& modulus); 57 | 58 | /* 59 | Computes the inverse of *this in the field GF(prime). 60 | If prime is not a prime number, the behavior is undefined. 61 | */ 62 | BigInt InvModPrime(const BigInt& prime) const; 63 | 64 | /* 65 | Return pair of the form (result, underflow_occurred). 66 | */ 67 | static constexpr std::pair Sub(const BigInt& a, const BigInt& b); 68 | 69 | constexpr bool operator<(const BigInt& b) const; 70 | 71 | constexpr bool operator>=(const BigInt& b) const { return !(*this < b); } 72 | 73 | constexpr bool operator>(const BigInt& b) const { return b < *this; } 74 | 75 | constexpr bool operator<=(const BigInt& b) const { return !(*this > b); } 76 | 77 | /* 78 | Returns the pair (q, r) such that this == q*divisor + r and r < divisor. 79 | */ 80 | std::pair Div(const BigInt& divisor) const; 81 | 82 | /* 83 | Returns the representation of the number as a string of the form "0x...". 84 | */ 85 | std::string ToString() const; 86 | 87 | std::vector ToBoolVector() const; 88 | 89 | /* 90 | Returns (x % target) assuming x is in the range [0, 2*target). 91 | 92 | The function assumes that target.NumLeadingZeros() > 0. 93 | 94 | Typically used after a Montgomery reduction which produces an output that 95 | satisfies the range requirement above. 96 | */ 97 | static constexpr BigInt ReduceIfNeeded(const BigInt& x, const BigInt& target); 98 | 99 | /* 100 | Calculates x*y/2^256 mod modulus, assuming that montgomery_mprime is 101 | (-(modulus^-1)) mod 2^64. Assumes that modulus.NumLeadingZeros() > 0. 102 | */ 103 | static constexpr BigInt MontMul( 104 | const BigInt& x, const BigInt& y, const BigInt& modulus, uint64_t montgomery_mprime); 105 | 106 | constexpr bool operator==(const BigInt& other) const; 107 | 108 | constexpr bool operator!=(const BigInt& other) const { return !(*this == other); } 109 | 110 | constexpr uint64_t& operator[](int i) { return gsl::at(value_, i); } 111 | 112 | constexpr const uint64_t& operator[](int i) const { return gsl::at(value_, i); } 113 | 114 | static constexpr size_t LimbCount() { return N; } 115 | 116 | /* 117 | Returns the number of leading zero's. 118 | */ 119 | constexpr size_t NumLeadingZeros() const; 120 | 121 | private: 122 | std::array value_; 123 | }; 124 | 125 | template 126 | std::ostream& operator<<(std::ostream& os, const BigInt& bigint); 127 | 128 | } // namespace starkware 129 | 130 | /* 131 | Implements the user defined _Z literal that constructs a BigInt of an 132 | arbitrary size. For example: BigInt<4> a = 133 | 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001_Z; 134 | */ 135 | template 136 | static constexpr auto operator"" _Z(); 137 | 138 | #include "starkware/algebra/big_int.inl" 139 | 140 | #endif // STARKWARE_ALGEBRA_BIG_INT_H_ 141 | -------------------------------------------------------------------------------- /src/starkware/algebra/big_int.inl: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "starkware/utils/math.h" 8 | 9 | namespace starkware { 10 | 11 | template 12 | BigInt BigInt::RandomBigInt(Prng* prng) { 13 | std::array value{}; 14 | for (size_t i = 0; i < N; ++i) { 15 | gsl::at(value, i) = prng->RandomUint64(); 16 | } 17 | return BigInt(value); 18 | } 19 | 20 | template 21 | template 22 | constexpr BigInt::BigInt(const BigInt& v) noexcept : value_{} { 23 | static_assert(N > K, "trimming is not supported"); 24 | for (size_t i = 0; i < K; ++i) { 25 | gsl::at(value_, i) = v[i]; 26 | } 27 | 28 | for (size_t i = K; i < N; ++i) { 29 | gsl::at(value_, i) = 0; 30 | } 31 | } 32 | 33 | template 34 | constexpr std::pair, bool> BigInt::Add(const BigInt& a, const BigInt& b) { 35 | bool carry{}; 36 | BigInt r{0}; 37 | 38 | for (size_t i = 0; i < N; ++i) { 39 | __uint128_t res = static_cast<__uint128_t>(a[i]) + b[i] + carry; 40 | carry = (res >> 64) != static_cast<__uint128_t>(0); 41 | r[i] = static_cast(res); 42 | } 43 | 44 | return std::make_pair(r, carry); 45 | } 46 | 47 | template 48 | constexpr BigInt<2 * N> BigInt::operator*(const BigInt& other) const { 49 | constexpr auto kResSize = 2 * N; 50 | BigInt final_res = BigInt::Zero(); 51 | // Multiply this by other using long multiplication algorithm. 52 | for (size_t i = 0; i < N; ++i) { 53 | uint64_t carry = static_cast(0U); 54 | for (size_t j = 0; j < N; ++j) { 55 | // For M == UINT64_MAX, we have: a*b+c+d <= M*M + 2M = (M+1)^2 - 1 == 56 | // UINT128_MAX. So we can do a multiplication and an addition without an 57 | // overflow. 58 | __uint128_t res = Umul128((*this)[j], other[i]) + final_res[i + j] + carry; 59 | carry = gsl::narrow_cast(res >> 64); 60 | final_res[i + j] = gsl::narrow_cast(res); 61 | } 62 | final_res[i + N] = static_cast(carry); 63 | } 64 | return final_res; 65 | } 66 | 67 | template 68 | BigInt BigInt::MulMod(const BigInt& a, const BigInt& b, const BigInt& modulus) { 69 | const BigInt<2 * N> mul_res = a * b; 70 | const BigInt<2 * N> mul_res_mod = mul_res.Div(BigInt<2 * N>(modulus)).second; 71 | 72 | BigInt res = Zero(); 73 | 74 | // Trim mul_res_mod to the N lower limbs (this is possible since it must be smaller than modulus). 75 | for (size_t i = 0; i < N; ++i) { 76 | res[i] = mul_res_mod[i]; 77 | } 78 | 79 | return res; 80 | } 81 | 82 | template 83 | BigInt BigInt::InvModPrime(const BigInt& prime) const { 84 | ASSERT(*this != BigInt::Zero(), "Inverse of 0 is not defined."); 85 | return GenericPow( 86 | *this, (prime - BigInt(2)).ToBoolVector(), BigInt::One(), 87 | [&prime](const BigInt& multiplier, BigInt* dst) { *dst = MulMod(*dst, multiplier, prime); }); 88 | } 89 | 90 | template 91 | constexpr std::pair, bool> BigInt::Sub(const BigInt& a, const BigInt& b) { 92 | bool carry{}; 93 | BigInt r{}; 94 | 95 | for (size_t i = 0; i < N; ++i) { 96 | __uint128_t res = static_cast<__uint128_t>(a[i]) - b[i] - carry; 97 | carry = (res >> 127) != static_cast<__uint128_t>(0); 98 | r[i] = static_cast(res); 99 | } 100 | 101 | return std::make_pair(r, carry); 102 | } 103 | 104 | template 105 | constexpr bool BigInt::operator<(const BigInt& b) const { 106 | return Sub(*this, b).second; 107 | } 108 | 109 | template 110 | std::pair, BigInt> BigInt::Div(const BigInt& divisor) const { 111 | // This is a simple long-division implementation. It is not very efficient and can be improved 112 | // if this function becomes a bottleneck. 113 | ASSERT(divisor != BigInt::Zero(), "Divisor must not be zero."); 114 | 115 | bool carry{}; 116 | BigInt res{}; 117 | BigInt shifted_divisor{}, tmp{}; 118 | BigInt a = *this; 119 | 120 | while (a >= divisor) { 121 | tmp = divisor; 122 | int shift = -1; 123 | do { 124 | shifted_divisor = tmp; 125 | shift++; 126 | std::tie(tmp, carry) = Add(shifted_divisor, shifted_divisor); 127 | } while (!carry && tmp <= a); 128 | 129 | a = Sub(a, shifted_divisor).first; 130 | res[shift / 64] |= Pow2(shift % 64); 131 | } 132 | 133 | return {res, a}; 134 | } 135 | 136 | template 137 | std::string BigInt::ToString() const { 138 | std::ostringstream res; 139 | res << "0x"; 140 | for (int i = N - 1; i >= 0; --i) { 141 | res << std::setfill('0') << std::setw(16) << std::hex << (*this)[i]; 142 | } 143 | return res.str(); 144 | } 145 | 146 | template 147 | std::vector BigInt::ToBoolVector() const { 148 | std::vector res; 149 | for (uint64_t value : value_) { 150 | for (int i = 0; i < std::numeric_limits::digits; ++i) { 151 | res.push_back((value & 1) != 0); 152 | value >>= 1; 153 | } 154 | } 155 | return res; 156 | } 157 | 158 | template 159 | constexpr bool BigInt::operator==(const BigInt& other) const { 160 | for (size_t i = 0; i < N; ++i) { 161 | if (gsl::at(value_, i) != gsl::at(other.value_, i)) { 162 | return false; 163 | } 164 | } 165 | return true; 166 | } 167 | 168 | template 169 | constexpr BigInt BigInt::ReduceIfNeeded(const BigInt& x, const BigInt& target) { 170 | ASSERT(target.NumLeadingZeros() > 0, "target must have at least one leading zero."); 171 | return (x >= target) ? x - target : x; 172 | } 173 | 174 | template 175 | constexpr BigInt BigInt::MontMul( 176 | const BigInt& x, const BigInt& y, const BigInt& modulus, uint64_t montgomery_mprime) { 177 | BigInt res{}; 178 | ASSERT(modulus.NumLeadingZeros() > 0, "We require at least one leading zero in the modulus"); 179 | ASSERT(y < modulus, "y is supposed to be smaller then the modulus"); 180 | ASSERT(x < modulus, "x is supposed to be smaller then the modulus."); 181 | for (size_t i = 0; i < N; ++i) { 182 | __uint128_t temp = Umul128(x[i], y[0]) + res[0]; 183 | uint64_t u_i = gsl::narrow_cast(temp) * montgomery_mprime; 184 | uint64_t carry1 = 0, carry2 = 0; 185 | 186 | for (size_t j = 0; j < N; ++j) { 187 | if (j != 0) { 188 | temp = Umul128(x[i], y[j]) + res[j]; 189 | } 190 | uint64_t low = carry1 + gsl::narrow_cast(temp); 191 | carry1 = gsl::narrow_cast(temp >> 64) + static_cast(low < carry1); 192 | temp = Umul128(modulus[j], u_i) + carry2; 193 | res[j] = low + gsl::narrow_cast(temp); 194 | carry2 = gsl::narrow_cast(temp >> 64) + static_cast(res[j] < low); 195 | } 196 | for (size_t j = 0; j < N - 1; ++j) { 197 | res[j] = res[j + 1]; 198 | } 199 | res[N - 1] = carry1 + carry2; 200 | ASSERT(res[N - 1] >= carry1, "There shouldn't be a carry here."); 201 | } 202 | return ReduceIfNeeded(res, modulus); 203 | } 204 | 205 | template 206 | constexpr size_t BigInt::NumLeadingZeros() const { 207 | int i = value_.size() - 1; 208 | size_t res = 0; 209 | 210 | while (i >= 0 && (gsl::at(value_, i) == 0)) { 211 | i--; 212 | res += std::numeric_limits::digits; 213 | } 214 | 215 | if (i >= 0) { 216 | res += __builtin_clzll(gsl::at(value_, i)); 217 | } 218 | 219 | return res; 220 | } 221 | 222 | template 223 | std::ostream& operator<<(std::ostream& os, const BigInt& bigint) { 224 | return os << bigint.ToString(); 225 | } 226 | 227 | namespace bigint { 228 | namespace details { 229 | /* 230 | Converts an hex digit ASCII char to the corresponding int. 231 | Assumes the input is an hex digit. 232 | */ 233 | inline constexpr uint64_t HexCharToUint64(char c) { 234 | if ('0' <= c && c <= '9') { 235 | return c - '0'; 236 | } 237 | 238 | if ('A' <= c && c <= 'F') { 239 | return c - 'A' + 10; 240 | } 241 | 242 | // The function assumes that the input is an hex digit, so we can assume 'a' 243 | // <= c && c <= 'f' here. 244 | return c - 'a' + 10; 245 | } 246 | 247 | template 248 | constexpr auto HexCharArrayToBigInt() { 249 | constexpr size_t kLen = sizeof...(Chars); 250 | constexpr std::array kDigits{Chars...}; 251 | static_assert(kDigits[0] == '0' && kDigits[1] == 'x', "Only hex input is currently supported"); 252 | 253 | constexpr size_t kNibblesPerUint64 = 2 * sizeof(uint64_t); 254 | constexpr size_t kResLen = (kLen - 2 + kNibblesPerUint64 - 1) / (kNibblesPerUint64); 255 | std::array res{}; 256 | 257 | for (size_t i = 0; i < kDigits.size() - 2; ++i) { 258 | const size_t limb = i / kNibblesPerUint64; 259 | const size_t nibble_offset = i % kNibblesPerUint64; 260 | const uint64_t nibble = HexCharToUint64(gsl::at(kDigits, kDigits.size() - i - 1)); 261 | 262 | gsl::at(res, limb) |= nibble << (4 * nibble_offset); 263 | } 264 | 265 | return BigInt(res); 266 | } 267 | } // namespace details 268 | } // namespace bigint 269 | 270 | template 271 | static constexpr auto operator"" _Z() { 272 | // This function is implemented as wrapper that calls the actual 273 | // implementation and stores it in a constexpr variable as we want to force 274 | // the evaluation to be done in compile time. We need to have the function 275 | // call because "constexpr auto kRes = BigInt(res);" won't work 276 | // unless res is constexpr. 277 | 278 | // Note that the compiler allows HEX and decimal literals but in any case 279 | // it enforces that Chars... contains only HEX (or decimal) characters. 280 | constexpr auto kRes = bigint::details::HexCharArrayToBigInt(); 281 | return kRes; 282 | } 283 | 284 | } // namespace starkware 285 | -------------------------------------------------------------------------------- /src/starkware/algebra/big_int_test.cc: -------------------------------------------------------------------------------- 1 | #include "starkware/algebra/big_int.h" 2 | 3 | #include 4 | 5 | #include "gmock/gmock.h" 6 | #include "gtest/gtest.h" 7 | 8 | #include "starkware/utils/test_utils.h" 9 | 10 | namespace starkware { 11 | namespace { 12 | 13 | using testing::HasSubstr; 14 | 15 | TEST(BigInt, Random) { 16 | Prng prng; 17 | for (size_t i = 0; i < 100; ++i) { 18 | BigInt<2> a = BigInt<2>::RandomBigInt(&prng); 19 | BigInt<2> b = BigInt<2>::RandomBigInt(&prng); 20 | ASSERT_NE(a, b); 21 | } 22 | } 23 | 24 | TEST(BigInt, Div) { 25 | BigInt<2> a({0, 1}); 26 | BigInt<2> b({5, 0}); 27 | 28 | EXPECT_EQ(a.Div(b), std::make_pair(BigInt<2>({0x3333333333333333L, 0}), BigInt<2>({1, 0}))); 29 | } 30 | 31 | TEST(BigInt, DivByZero) { 32 | BigInt<2> a({0, 1}); 33 | 34 | EXPECT_ASSERT(a.Div(BigInt<2>(0)), testing::HasSubstr("must not be zero")); 35 | } 36 | 37 | TEST(BigInt, DivRandom) { 38 | Prng prng; 39 | const BigInt<2> a = BigInt<2>::RandomBigInt(&prng); 40 | const BigInt<2> b = BigInt<2>::RandomBigInt(&prng); 41 | const auto& [q, r] = a.Div(b); 42 | EXPECT_EQ(BigInt<4>(a), q * b + r); 43 | EXPECT_LT(r, b); 44 | } 45 | 46 | TEST(BigInt, DivNoRemainder) { 47 | BigInt<2> a({20, 15}); 48 | BigInt<2> b({5, 0}); 49 | 50 | EXPECT_EQ(a.Div(b), std::make_pair(BigInt<2>({4, 3}), BigInt<2>({0, 0}))); 51 | } 52 | 53 | TEST(BigInt, NumLeadingZeros) { 54 | constexpr BigInt<5> kOne = BigInt<5>::One(); 55 | 56 | static_assert( 57 | BigInt<5>::kDigits - 1 == kOne.NumLeadingZeros(), 58 | "All digit except the last one should be zero"); 59 | static_assert((-kOne).NumLeadingZeros() == 0, "Should have 0 leading zero's"); 60 | 61 | static_assert(BigInt<5>::kDigits == BigInt<5>::Zero().NumLeadingZeros(), "All the digits are 0"); 62 | 63 | EXPECT_EQ(BigInt<5>({17, 0, 0, 0, 0}).NumLeadingZeros(), BigInt<5>::kDigits - 5U); 64 | EXPECT_EQ(BigInt<5>({0, 4, 0, 0, 0}).NumLeadingZeros(), BigInt<5>::kDigits - 67U); 65 | EXPECT_EQ(BigInt<5>({0, 1, 0, 0, 17}).NumLeadingZeros(), 59U); 66 | EXPECT_EQ(BigInt<5>({0, 1, 0, 0, 1}).NumLeadingZeros(), 63U); 67 | static_assert(BigInt<5>(7).NumLeadingZeros() == BigInt<5>::kDigits - 3U, "Should work."); 68 | } 69 | 70 | TEST(BigInt, Multiplication) { 71 | constexpr size_t kN1 = 1; 72 | constexpr size_t kN2 = 2; 73 | constexpr size_t kN3 = 10; 74 | 75 | static_assert( 76 | BigInt::Zero() * BigInt::Zero() == BigInt<2 * kN1>::Zero(), "0*0 is not 0"); 77 | static_assert( 78 | BigInt::Zero() * BigInt::Zero() == BigInt<2 * kN3>::Zero(), "0*0 is not 0"); 79 | static_assert(BigInt::One() * BigInt::One() == BigInt<2 * kN2>::One(), "1*1 is not 1"); 80 | static_assert( 81 | BigInt(Pow2(23)) * BigInt(Pow2(27)) == BigInt<2 * kN1>(Pow2(50)), 82 | "(2^23)*(2^27) is not 2^50"); 83 | static_assert(BigInt({0, 17}) * BigInt({0, 15}) == BigInt<2 * kN2>({0, 0, 255, 0})); 84 | static_assert( 85 | 0x45467f1b1b72b92a_Z * 0x5a24f03a01d5b10c_Z == 0x1864c79b3117812a6d564ff0d558b7f8_Z); 86 | static_assert( 87 | 0x5342b50c88dbce0db6fe1c672256eb8d_Z * 0xf42ff50167e9c6cca4d5b18636b1516e_Z == 88 | 0x4f6b2d7e7c1233fdc642edeefc766bc635729fa19af730c8cf66b2c4dc5dd396_Z, 89 | "Multiplication is wrong."); 90 | static_assert( 91 | 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_Z * 92 | 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_Z == 93 | BigInt<8>({0x1, 0x0, 0x0, 0x0, 0xfffffffffffffffe, 0xffffffffffffffff, 0xffffffffffffffff, 94 | 0xffffffffffffffff}), 95 | "Multiplication is wrong."); 96 | } 97 | 98 | TEST(BigInt, MulMod) { 99 | const BigInt<4> minus_one = -BigInt<4>::One(); 100 | const BigInt<4> m = BigInt<4>(8); 101 | const BigInt<4> res = BigInt<4>::MulMod(minus_one, minus_one, m); 102 | EXPECT_EQ(res, BigInt<4>(1)); 103 | 104 | EXPECT_EQ(BigInt<4>::MulMod(BigInt<4>(7), BigInt<4>(5), BigInt<4>(32)), BigInt<4>(3)); 105 | } 106 | 107 | TEST(BigInt, InvMod) { 108 | Prng prng; 109 | const auto val = BigInt<4>::RandomBigInt(&prng); 110 | const auto prime = 0xf04a65fa008b9e14bfe07094f9ff9bb7363ae6512e213a0a104adb17fb81b385_Z; 111 | const auto inv = val.InvModPrime(prime); 112 | EXPECT_EQ(BigInt<4>::MulMod(val, inv, prime), 0x1_Z); 113 | } 114 | 115 | TEST(BigInt, InvMod_Zero) { 116 | const auto prime = 0xf04a65fa008b9e14bfe07094f9ff9bb7363ae6512e213a0a104adb17fb81b385_Z; 117 | EXPECT_ASSERT(BigInt<4>::Zero().InvModPrime(prime), HasSubstr("Inverse of 0")); 118 | } 119 | 120 | TEST(BigInt, UserLiteral) { 121 | auto a = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001_Z; 122 | BigInt<4> b({0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48}); 123 | 124 | static_assert(std::is_same::value, "decltype(a) should be BigInt<4>"); 125 | 126 | ASSERT_EQ(a, b); 127 | } 128 | 129 | constexpr BigInt<1> BigWithVal(uint64_t val) { 130 | BigInt<1> res{0}; 131 | res[0] = val; 132 | return res; 133 | } 134 | 135 | TEST(BigInt, ConstexprTest) { 136 | constexpr auto kVal = 0x18_Z; 137 | 138 | static_assert(kVal == 0x18_Z, "Two identical numbers should be equal"); 139 | static_assert(kVal != 0x27_Z, "Two different numbers shouldn't be equal"); 140 | 141 | static_assert(kVal[0] == 0x18, "Two identical numbers should be equal"); 142 | static_assert(std::is_same::value, "Bad type"); 143 | 144 | static_assert(BigWithVal(13)[0] == 13, "should be equal"); 145 | 146 | static_assert(Umul128(13, 4) == __uint128_t(52), "should be equal"); 147 | static_assert(BigInt<2>(46) < BigInt<2>(87), "should work"); 148 | static_assert(BigInt<2>(146) >= BigInt<2>(87), "should work"); 149 | } 150 | 151 | TEST(BigInt, BigIntWidening) { 152 | ASSERT_EQ(BigInt<2>({0xffffffff00000001, 0}), 0xffffffff00000001_Z); 153 | ASSERT_EQ(BigInt<3>({0xffffffff00000001, 0x17, 0}), BigInt<2>({0xffffffff00000001, 0x17})); 154 | } 155 | 156 | TEST(BigInt, ConstexprBigIntWidening) { 157 | static_assert(BigInt<2>(0x1_Z) != BigInt<2>(0x2_Z), "should not be equal"); 158 | } 159 | 160 | TEST(BigInt, UserLiteral2) { 161 | auto zero = 0x0_Z; 162 | BigInt<1> one_limb_zero(0); 163 | BigInt<2> two_limb_zero(0); 164 | 165 | static_assert( 166 | std::is_same::value, 167 | "decltype(zero) should be BigInt<1>"); 168 | static_assert( 169 | !std::is_same::value, 170 | "decltype(zero) shouldn't be BigInt<2>"); 171 | 172 | ASSERT_EQ(one_limb_zero, zero); 173 | ASSERT_EQ(two_limb_zero, 174 | zero); // Works due to implicit cast of BigInt<2> to BigInt<1>. 175 | } 176 | 177 | } // namespace 178 | } // namespace starkware 179 | -------------------------------------------------------------------------------- /src/starkware/algebra/elliptic_curve.h: -------------------------------------------------------------------------------- 1 | #ifndef STARKWARE_ALGEBRA_ELLIPTIC_CURVE_H_ 2 | #define STARKWARE_ALGEBRA_ELLIPTIC_CURVE_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "third_party/gsl/gsl-lite.hpp" 10 | 11 | #include "starkware/algebra/big_int.h" 12 | 13 | namespace starkware { 14 | 15 | using std::size_t; 16 | 17 | /* 18 | Represents a point on an elliptic curve of the form: y^2 = x^3 + alpha*x + beta. 19 | */ 20 | template 21 | class EcPoint { 22 | public: 23 | constexpr EcPoint(const FieldElementT& x, const FieldElementT& y) : x(x), y(y) {} 24 | 25 | bool operator==(const EcPoint& rhs) const { return x == rhs.x && y == rhs.y; } 26 | bool operator!=(const EcPoint& rhs) const { return !(*this == rhs); } 27 | 28 | /* 29 | Computes the point added to itself. 30 | */ 31 | EcPoint Double(const FieldElementT& alpha) const; 32 | 33 | /* 34 | Returns the sum of two points. The added point must be different than both the original point 35 | and its negation. 36 | */ 37 | EcPoint operator+(const EcPoint& rhs) const; 38 | EcPoint operator-() const { return EcPoint(x, -y); } 39 | EcPoint operator-(const EcPoint& rhs) const { return (*this) + (-rhs); } 40 | 41 | /* 42 | Returns a random point on the curve: y^2 = x^3 + alpha*x + beta. 43 | */ 44 | static EcPoint Random(const FieldElementT& alpha, const FieldElementT& beta, Prng* prng); 45 | 46 | /* 47 | Returns one of the two points with the given x coordinate or nullopt if there is no such point. 48 | */ 49 | static std::optional GetPointFromX( 50 | const FieldElementT& x, const FieldElementT& alpha, const FieldElementT& beta); 51 | 52 | template 53 | EcPoint ConvertTo() const; 54 | 55 | /* 56 | Given the bool vector representing a scalar, and the alpha of the elliptic curve 57 | "y^2 = x^3 + alpha * x + beta" the point is on, returns scalar*point. 58 | */ 59 | template 60 | EcPoint MultiplyByScalar( 61 | const BigInt& scalar, const FieldElementT& alpha) const; 62 | 63 | FieldElementT x; 64 | FieldElementT y; 65 | 66 | private: 67 | /* 68 | Returns the sum of this point with a point in the form of std::optional, where std::nullopt 69 | represents the curve's zero element. 70 | */ 71 | std::optional> AddOptionalPoint( 72 | const std::optional>& point, const FieldElementT& alpha) const; 73 | }; 74 | 75 | } // namespace starkware 76 | 77 | #include "starkware/algebra/elliptic_curve.inl" 78 | 79 | #endif // STARKWARE_ALGEBRA_ELLIPTIC_CURVE_H_ 80 | -------------------------------------------------------------------------------- /src/starkware/algebra/elliptic_curve.inl: -------------------------------------------------------------------------------- 1 | #include "starkware/utils/error_handling.h" 2 | 3 | namespace starkware { 4 | 5 | template 6 | auto EcPoint::Double(const FieldElementT& alpha) const -> EcPoint { 7 | // Doubling a point cannot be done by adding the point to itself with the function AddPoints 8 | // because this function assumes that it gets distinct points. Usually, in order to sum two 9 | // points, one should draw a straight line containing these points, find the third point in the 10 | // intersection of the line and the curve, and then negate the y coordinate. In the special case 11 | // where the two points are the same point, one should draw the line that intersects the elliptic 12 | // curve "twice" at that point. This means that the slope of the line should be equal to the slope 13 | // of the curve at this point. That is, the derivative of the function 14 | // y = sqrt(x^3 + alpha * x + beta), which is slope = dy/dx = (3 * x^2 + alpha)/(2 * y). Note that 15 | // if y = 0 then the point is a 2-torsion (doubling it gives infinity). The line is then given by 16 | // y = slope * x + y_intercept. The third intersection point is found using the equation that is 17 | // true for all cases: slope^2 = x_1 + x_2 + x_3 (where x_1, x_2 and x_3 are the x coordinates of 18 | // three points in the intersection of the curve with a line). 19 | ASSERT(y != FieldElementT::Zero(), "Tangent slope of 2 torsion point is infinite."); 20 | const auto x_squared = x * x; 21 | const FieldElementT tangent_slope = (x_squared + x_squared + x_squared + alpha) / (y + y); 22 | const FieldElementT x2 = tangent_slope * tangent_slope - (x + x); 23 | const FieldElementT y2 = tangent_slope * (x - x2) - y; 24 | return {x2, y2}; 25 | } 26 | 27 | template 28 | auto EcPoint::operator+(const EcPoint& rhs) const -> EcPoint { 29 | ASSERT(this->x != rhs.x, "x values should be different for arbitrary points"); 30 | // To sum two points, one should draw a straight line containing these points, find the 31 | // third point in the intersection of the line and the curve, and then negate the y coordinate. 32 | // Notice that if x_1 = x_2 then either they are the same point or their sum is infinity. This 33 | // function doesn't deal with these cases. The straight line is given by the equation: 34 | // y = slope * x + y_intercept. The x coordinate of the third point is found by solving the system 35 | // of equations: 36 | 37 | // y = slope * x + y_intercept 38 | // y^2 = x^3 + alpha * x + beta 39 | 40 | // These equations yield: 41 | // (slope * x + y_intercept)^2 = x^3 + alpha * x + beta 42 | // ==> x^3 - slope^2 * x^2 + (alpha - 2 * slope * y_intercept) * x + (beta - y_intercept^2) = 0 43 | 44 | // This is a monic polynomial in x whose roots are exactly the x coordinates of the three 45 | // intersection points of the line with the curve. Thus it is equal to the polynomial: 46 | // (x - x_1) * (x - x_2) * (x - x_3) 47 | // where x1, x2, x3 are the x coordinates of those points. 48 | // Notice that the equality of the coefficient of the x^2 term yields: 49 | // slope^2 = x_1 + x_2 + x_3. 50 | const FieldElementT slope = (this->y - rhs.y) / (this->x - rhs.x); 51 | const FieldElementT x3 = slope * slope - this->x - rhs.x; 52 | const FieldElementT y3 = slope * (this->x - x3) - this->y; 53 | return {x3, y3}; 54 | } 55 | 56 | template 57 | auto EcPoint::GetPointFromX( 58 | const FieldElementT& x, const FieldElementT& alpha, const FieldElementT& beta) 59 | -> std::optional { 60 | const FieldElementT y_squared = x * x * x + alpha * x + beta; 61 | if (!y_squared.IsSquare()) { 62 | return std::nullopt; 63 | } 64 | return {{x, y_squared.Sqrt()}}; 65 | } 66 | 67 | template 68 | auto EcPoint::Random( 69 | const FieldElementT& alpha, const FieldElementT& beta, Prng* prng) -> EcPoint { 70 | // Each iteration has probability of ~1/2 to fail. Thus the probability of failing 100 iterations 71 | // is negligible. 72 | for (size_t i = 0; i < 100; ++i) { 73 | const FieldElementT x = FieldElementT::RandomElement(prng); 74 | const std::optional pt = GetPointFromX(x, alpha, beta); 75 | if (pt.has_value()) { 76 | // Change the sign of the returned y coordinate with probability 1/2. 77 | if (prng->RandomUint64(0, 1) == 1) { 78 | return -*pt; 79 | } 80 | return *pt; 81 | } 82 | } 83 | ASSERT(false, "No random point found."); 84 | } 85 | 86 | template 87 | template 88 | EcPoint EcPoint::ConvertTo() const { 89 | return EcPoint(OtherFieldElementT(x), OtherFieldElementT(y)); 90 | } 91 | 92 | template 93 | template 94 | EcPoint EcPoint::MultiplyByScalar( 95 | const BigInt& scalar, const FieldElementT& alpha) const { 96 | std::optional> res; 97 | EcPoint power = *this; 98 | for (const auto& b : scalar.ToBoolVector()) { 99 | if (b) { 100 | res = power.AddOptionalPoint(res, alpha); 101 | } 102 | // If power == -power, then power + power == zero, and will remain zero (so res will not 103 | // change) until the end of the for loop. Therefore there is no point to keep looping. 104 | if (power == -power) { 105 | break; 106 | } 107 | power = power.Double(alpha); 108 | } 109 | ASSERT(res.has_value(), "Result of multiplication is the curve's zero element."); 110 | return *res; 111 | } 112 | 113 | template 114 | std::optional> EcPoint::AddOptionalPoint( 115 | const std::optional>& point, const FieldElementT& alpha) const { 116 | if (!point) { 117 | return *this; 118 | } 119 | // If a == -b, then a+b == zero element. 120 | if (*point == -*this) { 121 | return std::nullopt; 122 | } 123 | if (*point == *this) { 124 | return point->Double(alpha); 125 | } 126 | return *point + *this; 127 | } 128 | 129 | } // namespace starkware 130 | -------------------------------------------------------------------------------- /src/starkware/algebra/elliptic_curve_test.cc: -------------------------------------------------------------------------------- 1 | #include "starkware/algebra/elliptic_curve.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "gmock/gmock.h" 8 | #include "gtest/gtest.h" 9 | 10 | #include "starkware/algebra/big_int.h" 11 | #include "starkware/algebra/fraction_field_element.h" 12 | #include "starkware/algebra/prime_field_element.h" 13 | #include "starkware/utils/prng.h" 14 | #include "starkware/utils/test_utils.h" 15 | 16 | namespace starkware { 17 | namespace { 18 | 19 | using testing::HasSubstr; 20 | 21 | TEST(EllipticCurve, DoublePointTest) { 22 | Prng prng; 23 | PrimeFieldElement x = PrimeFieldElement::RandomElement(&prng); 24 | PrimeFieldElement y = PrimeFieldElement::RandomElement(&prng); 25 | PrimeFieldElement alpha = PrimeFieldElement::RandomElement(&prng); 26 | // There is always a beta such that {x,y} is a point on the curve y^2 = x^3 + alpha * x + beta. 27 | EcPoint point = {x, y}; 28 | EcPoint point_times_4 = point.Double(alpha).Double(alpha); 29 | ASSERT_EQ(point_times_4, (point + point.Double(alpha)) + point); 30 | } 31 | 32 | TEST(EllipticCurve, MulByScalarTest) { 33 | Prng prng; 34 | const EcPoint point = {PrimeFieldElement::RandomElement(&prng), 35 | PrimeFieldElement::RandomElement(&prng)}; 36 | const PrimeFieldElement alpha = PrimeFieldElement::RandomElement(&prng); 37 | // There is always a beta such that {x,y} is a point on the curve y^2 = x^3 + alpha * x + beta. 38 | 39 | const EcPoint point_times_5_b = point.MultiplyByScalar(0x5_Z, alpha); 40 | ASSERT_EQ(point_times_5_b, point.Double(alpha).Double(alpha) + point); 41 | } 42 | 43 | TEST(EllipticCurve, MulByZeroTest) { 44 | Prng prng; 45 | const EcPoint point = {PrimeFieldElement::RandomElement(&prng), 46 | PrimeFieldElement::RandomElement(&prng)}; 47 | const PrimeFieldElement alpha = PrimeFieldElement::RandomElement(&prng); 48 | // There is always a beta such that {x,y} is a point on the curve y^2 = x^3 + alpha * x + beta. 49 | 50 | // Multiply by zero (result should be zero). 51 | EXPECT_ASSERT(point.MultiplyByScalar(0x0_Z, alpha), HasSubstr("zero element")); 52 | } 53 | 54 | TEST(EllipticCurve, MulPowerBecomesZeroTest) { 55 | Prng prng; 56 | // Start with a point P such that P+P = zero. 57 | const EcPoint point = {PrimeFieldElement::RandomElement(&prng), 58 | PrimeFieldElement::FromUint(0)}; 59 | const PrimeFieldElement alpha = PrimeFieldElement::FromUint(1); 60 | 61 | // Check that multiplying P by an odd scalar gives back P. 62 | ASSERT_EQ(point.MultiplyByScalar(0x3_Z, alpha), point); 63 | ASSERT_EQ(point.MultiplyByScalar(0x12431234234121_Z, alpha), point); 64 | 65 | // Check that multiplying P by an even scalar gives zero. 66 | EXPECT_ASSERT(point.MultiplyByScalar(0x4_Z, alpha), HasSubstr("zero element")); 67 | } 68 | 69 | TEST(EllipticCurve, PlusPointTest) { 70 | Prng prng; 71 | const PrimeFieldElement alpha = PrimeFieldElement::RandomElement(&prng); 72 | const PrimeFieldElement beta = PrimeFieldElement::RandomElement(&prng); 73 | const auto point1 = EcPoint::Random(alpha, beta, &prng); 74 | const auto point2 = EcPoint::Random(alpha, beta, &prng); 75 | const auto point3 = EcPoint::Random(alpha, beta, &prng); 76 | EXPECT_EQ(point1 + point2, point2 + point1); 77 | EXPECT_EQ((point1 + point2) + point3, point1 + (point2 + point3)); 78 | } 79 | 80 | TEST(EllipticCurve, MinusPointTest) { 81 | Prng prng; 82 | PrimeFieldElement x1 = PrimeFieldElement::RandomElement(&prng); 83 | PrimeFieldElement y1 = PrimeFieldElement::RandomElement(&prng); 84 | PrimeFieldElement alpha = PrimeFieldElement::RandomElement(&prng); 85 | PrimeFieldElement x2 = PrimeFieldElement::RandomElement(&prng); 86 | PrimeFieldElement y2 = PrimeFieldElement::RandomElement(&prng); 87 | // There is always a beta such that {x,y} is a point on the curve y^2 = x^3 + alpha * x + beta. 88 | EcPoint point1 = {x1, y1}; 89 | EcPoint point2 = {x2, y2}; 90 | EcPoint point1_times_2 = point1.Double(alpha); 91 | ASSERT_EQ(point1_times_2 - point1, point1); 92 | ASSERT_EQ((point1 + point2) - point1, point2); 93 | } 94 | 95 | TEST(EllipticCurve, RandomPointOnCurve) { 96 | Prng prng; 97 | const PrimeFieldElement alpha = PrimeFieldElement::RandomElement(&prng); 98 | const PrimeFieldElement beta = PrimeFieldElement::RandomElement(&prng); 99 | const auto point = EcPoint::Random(alpha, beta, &prng); 100 | // Verifies that the point is on the curve. 101 | EXPECT_TRUE(point.y * point.y == point.x * point.x * point.x + alpha * point.x + beta); 102 | 103 | const auto point2 = EcPoint::GetPointFromX(point.x, alpha, beta); 104 | ASSERT_TRUE(point2.has_value()); 105 | EXPECT_EQ(point.x, point2->x); 106 | EXPECT_TRUE(point.y == point2->y || point.y == -point2->y); 107 | } 108 | 109 | TEST(EllipticCurve, TestConvertTo) { 110 | Prng prng; 111 | const PrimeFieldElement first_element = PrimeFieldElement::RandomElement(&prng); 112 | const PrimeFieldElement second_element = PrimeFieldElement::RandomElement(&prng); 113 | const auto random_ec_point = EcPoint(first_element, second_element); 114 | const auto ec_point_converted = 115 | random_ec_point.template ConvertTo>(); 116 | EXPECT_EQ(random_ec_point, ec_point_converted.template ConvertTo()); 117 | } 118 | 119 | } // namespace 120 | } // namespace starkware 121 | -------------------------------------------------------------------------------- /src/starkware/algebra/fraction_field_element.h: -------------------------------------------------------------------------------- 1 | #ifndef STARKWARE_ALGEBRA_FRACTION_FIELD_ELEMENT_H_ 2 | #define STARKWARE_ALGEBRA_FRACTION_FIELD_ELEMENT_H_ 3 | 4 | #include "starkware/utils/error_handling.h" 5 | #include "starkware/utils/prng.h" 6 | 7 | namespace starkware { 8 | 9 | /* 10 | Represents a field element as an element of the fraction field of the original field. The elements 11 | of the fraction field are a/b for a,b in the original field, and b != 0. The representation of 12 | a FieldElementT b is b/1. Addition and multiplication for the fraction field are defined naturally 13 | (see operator+ and operator*). The resulting field is isomorphic to the original field. This 14 | fractional representation of the original field enables to perform an inverse cheaply: the inverse 15 | of a/b is simply b/a. 16 | */ 17 | template 18 | class FractionFieldElement { 19 | public: 20 | explicit constexpr FractionFieldElement(const FieldElementT& num_val) 21 | : numerator_(num_val), denominator_(FieldElementT::One()) {} 22 | 23 | /* 24 | Creates a FractionFieldElement with the value num_val/denom_val. 25 | denom_val can't be zero. 26 | */ 27 | constexpr FractionFieldElement(const FieldElementT& num_val, const FieldElementT& denom_val) 28 | : numerator_(num_val), denominator_(denom_val) { 29 | ASSERT(denominator_ != FieldElementT::Zero(), "Denominator can't be zero."); 30 | } 31 | 32 | FractionFieldElement operator+(const FractionFieldElement& rhs) const; 33 | 34 | FractionFieldElement operator-(const FractionFieldElement& rhs) const; 35 | 36 | FractionFieldElement operator-() const { return FractionFieldElement(-numerator_, denominator_); } 37 | 38 | FractionFieldElement operator*(const FractionFieldElement& rhs) const; 39 | FractionFieldElement operator/(const FractionFieldElement& rhs) const { 40 | return *this * rhs.Inverse(); 41 | } 42 | 43 | bool operator==(const FractionFieldElement& rhs) const; 44 | bool operator!=(const FractionFieldElement& rhs) const { return !(*this == rhs); } 45 | 46 | FractionFieldElement Inverse() const; 47 | 48 | static constexpr FractionFieldElement Zero() { 49 | return FractionFieldElement(FieldElementT::Zero()); 50 | } 51 | 52 | static constexpr FractionFieldElement One() { return FractionFieldElement(FieldElementT::One()); } 53 | 54 | /* 55 | Returns a fraction field element: its numerator is a random FieldElementT generated by 56 | FieldElementT::RandomElement of and its denominator is FieldElementT::One(). 57 | */ 58 | static FractionFieldElement RandomElement(Prng* prng) { 59 | return FractionFieldElement(FieldElementT::RandomElement(prng)); 60 | } 61 | 62 | FieldElementT ToBaseFieldElement() const { return this->numerator_ * denominator_.Inverse(); } 63 | 64 | explicit operator FieldElementT() const { return ToBaseFieldElement(); } 65 | 66 | private: 67 | FieldElementT numerator_; 68 | FieldElementT denominator_; 69 | }; 70 | 71 | } // namespace starkware 72 | 73 | #include "starkware/algebra/fraction_field_element.inl" 74 | 75 | #endif // STARKWARE_ALGEBRA_FRACTION_FIELD_ELEMENT_H_ 76 | -------------------------------------------------------------------------------- /src/starkware/algebra/fraction_field_element.inl: -------------------------------------------------------------------------------- 1 | #include "starkware/algebra/fraction_field_element.h" 2 | 3 | #include "starkware/utils/error_handling.h" 4 | 5 | namespace starkware { 6 | 7 | template 8 | FractionFieldElement FractionFieldElement::operator+( 9 | const FractionFieldElement& rhs) const { 10 | const auto num_value = this->numerator_ * rhs.denominator_ + this->denominator_ * rhs.numerator_; 11 | const auto denom_value = this->denominator_ * rhs.denominator_; 12 | return FractionFieldElement(num_value, denom_value); 13 | } 14 | 15 | template 16 | FractionFieldElement FractionFieldElement::operator-( 17 | const FractionFieldElement& rhs) const { 18 | const auto num_value = this->numerator_ * rhs.denominator_ - this->denominator_ * rhs.numerator_; 19 | const auto denom_value = this->denominator_ * rhs.denominator_; 20 | return FractionFieldElement(num_value, denom_value); 21 | } 22 | 23 | template 24 | FractionFieldElement FractionFieldElement::operator*( 25 | const FractionFieldElement& rhs) const { 26 | return FractionFieldElement( 27 | this->numerator_ * rhs.numerator_, this->denominator_ * rhs.denominator_); 28 | } 29 | 30 | template 31 | bool FractionFieldElement::operator==( 32 | const FractionFieldElement& rhs) const { 33 | return this->numerator_ * rhs.denominator_ == this->denominator_ * rhs.numerator_; 34 | } 35 | 36 | template 37 | FractionFieldElement FractionFieldElement::Inverse() const { 38 | ASSERT(this->numerator_ != FieldElementT::Zero(), "Zero does not have an inverse"); 39 | return FractionFieldElement(denominator_, numerator_); 40 | } 41 | 42 | } // namespace starkware 43 | -------------------------------------------------------------------------------- /src/starkware/algebra/fraction_field_element_test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "starkware/algebra/fraction_field_element.h" 5 | #include "starkware/algebra/prime_field_element.h" 6 | #include "starkware/utils/test_utils.h" 7 | 8 | #include "gmock/gmock.h" 9 | #include "gtest/gtest.h" 10 | 11 | namespace starkware { 12 | namespace { 13 | 14 | using FractionFieldElementT = FractionFieldElement; 15 | 16 | FractionFieldElementT ElementFromInts(uint64_t numerator, uint64_t denominator) { 17 | const FractionFieldElementT num(PrimeFieldElement::FromUint(numerator)); 18 | const FractionFieldElementT denom(PrimeFieldElement::FromUint(denominator)); 19 | return num * denom.Inverse(); 20 | } 21 | 22 | TEST(FractionFieldElement, Equality) { 23 | FractionFieldElementT a = ElementFromInts(5, 10); 24 | FractionFieldElementT b = ElementFromInts(30, 60); 25 | FractionFieldElementT c = ElementFromInts(1, 3); 26 | FractionFieldElementT d(-PrimeFieldElement::One(), -PrimeFieldElement::FromUint(2)); 27 | EXPECT_TRUE(a == b); 28 | EXPECT_TRUE(a == d); 29 | EXPECT_FALSE(a != d); 30 | EXPECT_FALSE(a != b); 31 | EXPECT_FALSE(a == c); 32 | EXPECT_TRUE(a != c); 33 | EXPECT_FALSE(b == c); 34 | EXPECT_TRUE(b != c); 35 | } 36 | 37 | TEST(FractionFieldElement, Addition) { 38 | EXPECT_EQ(ElementFromInts(1, 3) + ElementFromInts(5, 4), ElementFromInts(19, 12)); 39 | } 40 | 41 | TEST(FractionFieldElement, UnaryMinus) { 42 | Prng prng; 43 | static constexpr uint64_t kMaxVal = std::numeric_limits::max(); 44 | EXPECT_EQ(FractionFieldElementT::Zero(), -FractionFieldElementT::Zero()); 45 | for (size_t i = 0; i < 100; ++i) { 46 | FractionFieldElementT x = 47 | ElementFromInts(prng.RandomUint64(1, kMaxVal), prng.RandomUint64(1, kMaxVal)); 48 | ASSERT_NE(x, -x); 49 | ASSERT_EQ(FractionFieldElementT::Zero(), -x + x); 50 | } 51 | } 52 | 53 | TEST(FractionFieldElement, Subtraction) { 54 | FractionFieldElementT a = ElementFromInts(5, 2); 55 | FractionFieldElementT b = ElementFromInts(1, 3); 56 | FractionFieldElementT c = ElementFromInts(13, 6); 57 | EXPECT_EQ(a - b, c); 58 | EXPECT_EQ(b - a, -c); 59 | } 60 | 61 | TEST(FractionFieldElement, Multiplication) { 62 | FractionFieldElementT a = ElementFromInts(1, 3); 63 | FractionFieldElementT b = ElementFromInts(6, 4); 64 | FractionFieldElementT c1 = ElementFromInts(6, 12); 65 | FractionFieldElementT c2 = ElementFromInts(1, 2); 66 | FractionFieldElementT c3 = ElementFromInts(2, 4); 67 | FractionFieldElementT a_mul_b = a * b; 68 | EXPECT_EQ(a_mul_b, c1); 69 | EXPECT_EQ(a_mul_b, c2); 70 | EXPECT_EQ(a_mul_b, c3); 71 | EXPECT_EQ(a, a_mul_b * b.Inverse()); 72 | } 73 | 74 | TEST(FractionFieldElement, Inverse) { 75 | Prng prng; 76 | FractionFieldElementT a = ElementFromInts(6, 4); 77 | FractionFieldElementT b = ElementFromInts(10, 1); 78 | FractionFieldElementT c = ElementFromInts(1, 1); 79 | FractionFieldElementT d = ElementFromInts(0, 1); 80 | FractionFieldElementT e = ElementFromInts(12, 18); 81 | FractionFieldElementT a_inv = a.Inverse(); 82 | FractionFieldElementT b_inv = b.Inverse(); 83 | FractionFieldElementT c_inv = c.Inverse(); 84 | FractionFieldElementT e_inv = e.Inverse(); 85 | FractionFieldElementT random = FractionFieldElementT::RandomElement(&prng); 86 | EXPECT_EQ(a_inv, e); 87 | EXPECT_EQ(a, e_inv); 88 | EXPECT_EQ(FractionFieldElementT::One(), a_inv * a); 89 | EXPECT_EQ(FractionFieldElementT::One(), b * b_inv); 90 | EXPECT_EQ(c, c_inv); 91 | EXPECT_EQ(random, random.Inverse().Inverse()); 92 | EXPECT_ASSERT(d.Inverse(), testing::HasSubstr("Zero does not have an inverse")); 93 | EXPECT_EQ(random * random.Inverse(), FractionFieldElementT::One()); 94 | } 95 | 96 | TEST(FractionFieldElement, Division) { 97 | FractionFieldElementT a = ElementFromInts(5, 10); 98 | FractionFieldElementT b = ElementFromInts(6, 4); 99 | FractionFieldElementT c = ElementFromInts(1, 3); 100 | FractionFieldElementT a_div_b = a / b; 101 | EXPECT_EQ(a_div_b, c); 102 | EXPECT_EQ(a, a_div_b * b); 103 | } 104 | 105 | TEST(FractionFieldElement, ToBaseFieldElement) { 106 | Prng prng; 107 | PrimeFieldElement a = PrimeFieldElement::RandomElement(&prng); 108 | PrimeFieldElement b = PrimeFieldElement::RandomElement(&prng); 109 | EXPECT_EQ(FractionFieldElementT(a).ToBaseFieldElement(), a); 110 | EXPECT_EQ( 111 | FractionFieldElementT(PrimeFieldElement::Zero()).ToBaseFieldElement(), 112 | PrimeFieldElement::Zero()); 113 | EXPECT_EQ( 114 | FractionFieldElementT(PrimeFieldElement::One()).ToBaseFieldElement(), 115 | PrimeFieldElement::One()); 116 | EXPECT_EQ( 117 | (FractionFieldElementT(a) / FractionFieldElementT(b)).ToBaseFieldElement(), a * b.Inverse()); 118 | } 119 | 120 | } // namespace 121 | } // namespace starkware 122 | -------------------------------------------------------------------------------- /src/starkware/algebra/prime_field_element.cc: -------------------------------------------------------------------------------- 1 | #include "starkware/algebra/prime_field_element.h" 2 | 3 | namespace starkware { 4 | 5 | PrimeFieldElement PrimeFieldElement::RandomElement(Prng* prng) { 6 | constexpr size_t kMostSignificantLimb = ValueType::LimbCount() - 1; 7 | static_assert( 8 | kModulus[kMostSignificantLimb] != 0, "We assume kModulus[kMostSignificantLimb] is not zero"); 9 | constexpr uint64_t kBitsMask = (Pow2(Log2Floor(kModulus[kMostSignificantLimb]) + 1)) - 1; 10 | 11 | PrimeFieldElement random_element = PrimeFieldElement::Zero(); 12 | do { 13 | random_element.value_ = ValueType::RandomBigInt(prng); 14 | random_element.value_[kMostSignificantLimb] &= kBitsMask; 15 | } while (random_element.value_ >= kModulus); // Required to enforce uniformity. 16 | 17 | return random_element; 18 | } 19 | 20 | PrimeFieldElement PrimeFieldElement::Pow(const std::vector& exponent_bits) const { 21 | return GenericPow( 22 | *this, exponent_bits, PrimeFieldElement::One(), 23 | [](const PrimeFieldElement& multiplier, PrimeFieldElement* dst) { 24 | *dst = *dst * multiplier; 25 | }); 26 | } 27 | 28 | PrimeFieldElement PrimeFieldElement::Pow(const uint64_t exponent) const { 29 | return Pow(BigInt<1>(exponent).ToBoolVector()); 30 | } 31 | 32 | bool PrimeFieldElement::IsSquare() const { 33 | if (*this == PrimeFieldElement::Zero()) { 34 | return true; 35 | } 36 | 37 | // value is a square if and only if value^((p-1) / 2) = 1. 38 | return Pow(kHalfMultiplicativeGroupSize.ToBoolVector()) == PrimeFieldElement::One(); 39 | } 40 | 41 | PrimeFieldElement PrimeFieldElement::Sqrt() const { 42 | if (*this == PrimeFieldElement::Zero()) { 43 | return PrimeFieldElement::Zero(); 44 | } 45 | 46 | // We use the following algorithm to compute the square root of the element: 47 | // Let v be the input, let +r and -r be the roots of v and consider the ring 48 | // R := F[x] / (x^2 - v). 49 | // 50 | // This ring is isomorphic to the ring F x F where the isomorphism is given by the map 51 | // a*x + b --> (ar + b, -ar + b) (recall that we don't know r, so we cannot compute this map). 52 | // 53 | // Pick a random element x + b in R, and compute (x + b)^((p-1)/2). Let's say that the result is 54 | // c*x + d. 55 | // Taking a random element in F to the power of (p-1)/2 gives +1 or -1 with probability 56 | // 0.5. Since R is isomorphic to F x F (where multiplication is pointwise), the result of the 57 | // computation will be one of the four pairs: 58 | // (+1, +1), (-1, -1), (+1, -1), (-1, +1). 59 | // 60 | // If the result is (+1, +1) or (-1, -1) (which are the elements (0*x + 1) and (0*x - 1) in R) - 61 | // try again with another random element. 62 | // 63 | // If the result is (+1, -1) then cr + d = 1 and -cr + d = -1. Therefore r = c^{-1} and d=0. In 64 | // the second case -r = c^{-1}. In both cases c^{-1} will be the returned root. 65 | 66 | // Store an element in R as a pair: first * x + second. 67 | using RingElement = std::pair; 68 | const RingElement one{PrimeFieldElement::Zero(), PrimeFieldElement::One()}; 69 | const RingElement minus_one{PrimeFieldElement::Zero(), -PrimeFieldElement::One()}; 70 | 71 | auto mult = [this](const RingElement& multiplier, RingElement* dst) { 72 | // Compute res * multiplier in the ring. 73 | auto res_first = multiplier.first * dst->second + multiplier.second * dst->first; 74 | auto res_second = multiplier.first * dst->first * *this + multiplier.second * dst->second; 75 | *dst = {res_first, res_second}; 76 | }; 77 | 78 | // Compute q = (p - 1) / 2 and get its bits. 79 | const std::vector q_bits = kHalfMultiplicativeGroupSize.ToBoolVector(); 80 | 81 | Prng prng; 82 | while (true) { 83 | // Pick a random element (x + b) in R. 84 | RingElement random_element{PrimeFieldElement::One(), PrimeFieldElement::RandomElement(&prng)}; 85 | 86 | // Compute the exponentiation: random_element ^ ((p-1) / 2). 87 | RingElement res = GenericPow(random_element, q_bits, one, mult); 88 | 89 | // If res is either 1 or -1, try again. 90 | if (res == one || res == minus_one) { 91 | continue; 92 | } 93 | 94 | const PrimeFieldElement root = res.first.Inverse(); 95 | 96 | ASSERT(root * root == *this, "value does not have a square root."); 97 | 98 | return root; 99 | } 100 | } 101 | 102 | } // namespace starkware 103 | -------------------------------------------------------------------------------- /src/starkware/algebra/prime_field_element.h: -------------------------------------------------------------------------------- 1 | #ifndef STARKWARE_ALGEBRA_PRIME_FIELD_ELEMENT_H_ 2 | #define STARKWARE_ALGEBRA_PRIME_FIELD_ELEMENT_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "starkware/algebra/big_int.h" 14 | #include "starkware/utils/error_handling.h" 15 | #include "starkware/utils/prng.h" 16 | 17 | namespace starkware { 18 | 19 | /* 20 | Represents an element of GF(p) for p = 2^251 + 17 * 2^192 + 1. 21 | The value is stored in Montgomery representation. 22 | */ 23 | class PrimeFieldElement { 24 | public: 25 | using ValueType = BigInt<4>; 26 | static constexpr ValueType kModulus = 27 | 0x800000000000011000000000000000000000000000000000000000000000001_Z; 28 | static constexpr ValueType kMontgomeryR = 29 | 0x7fffffffffffdf0ffffffffffffffffffffffffffffffffffffffffffffffe1_Z; 30 | static constexpr ValueType kMontgomeryRSquared = 31 | 0x7ffd4ab5e008810ffffffffff6f800000000001330ffffffffffd737e000401_Z; 32 | static constexpr ValueType kMontgomeryRCubed = 33 | 0x38e5f79873c0a6df47d84f8363000187545706677ffcc06cc7177d1406df18e_Z; 34 | static constexpr uint64_t kMontgomeryMPrime = ~uint64_t(0); 35 | static constexpr ValueType kHalfMultiplicativeGroupSize = 36 | 0x400000000000008800000000000000000000000000000000000000000000000_Z; 37 | 38 | PrimeFieldElement() = delete; 39 | 40 | static PrimeFieldElement FromUint(uint64_t val) { 41 | return PrimeFieldElement( 42 | // Note that because MontgomeryMul divides by r we need to multiply by r^2 here. 43 | MontgomeryMul(ValueType(val), kMontgomeryRSquared)); 44 | } 45 | 46 | static constexpr PrimeFieldElement FromBigInt(const ValueType& val) { 47 | return PrimeFieldElement( 48 | // Note that because MontgomeryMul divides by r we need to multiply by r^2 here. 49 | MontgomeryMul(val, kMontgomeryRSquared)); 50 | } 51 | 52 | static PrimeFieldElement RandomElement(Prng* prng); 53 | 54 | static constexpr PrimeFieldElement Zero() { return PrimeFieldElement(ValueType({})); } 55 | 56 | static constexpr PrimeFieldElement One() { return PrimeFieldElement(kMontgomeryR); } 57 | 58 | PrimeFieldElement operator*(const PrimeFieldElement& rhs) const { 59 | return PrimeFieldElement(MontgomeryMul(value_, rhs.value_)); 60 | } 61 | 62 | PrimeFieldElement operator+(const PrimeFieldElement& rhs) const { 63 | return PrimeFieldElement{ValueType::ReduceIfNeeded(value_ + rhs.value_, kModulus)}; 64 | } 65 | 66 | PrimeFieldElement operator-(const PrimeFieldElement& rhs) const { 67 | return PrimeFieldElement{(value_ >= rhs.value_) ? (value_ - rhs.value_) 68 | : (value_ + kModulus - rhs.value_)}; 69 | } 70 | 71 | PrimeFieldElement operator-() const { return Zero() - *this; } 72 | 73 | PrimeFieldElement operator/(const PrimeFieldElement& rhs) const { return *this * rhs.Inverse(); } 74 | 75 | bool operator==(const PrimeFieldElement& rhs) const { return value_ == rhs.value_; } 76 | bool operator!=(const PrimeFieldElement& rhs) const { return !(*this == rhs); } 77 | 78 | PrimeFieldElement Inverse() const { 79 | ASSERT(*this != PrimeFieldElement::Zero(), "Zero does not have an inverse"); 80 | return Pow((kModulus - 0x2_Z).ToBoolVector()); 81 | } 82 | 83 | /* 84 | Returns the power of a field element, where exponent_bits[0] is the least significant bit of the 85 | exponent. 86 | Note that this function doesn't support negative exponents. 87 | */ 88 | PrimeFieldElement Pow(const std::vector& exponent_bits) const; 89 | 90 | /* 91 | Returns the power of a field element for the given exponent. 92 | */ 93 | PrimeFieldElement Pow(const uint64_t exponent) const; 94 | 95 | /* 96 | For a field element x, returns true if there exists a field element y such that x = y^2. 97 | */ 98 | bool IsSquare() const; 99 | 100 | /* 101 | For a field element x, returns an element y such that y^2 = x. If no such y exists, the function 102 | throws an exception. 103 | */ 104 | PrimeFieldElement Sqrt() const; 105 | 106 | /* 107 | Returns the standard representation. 108 | 109 | A value in the range [0, kBigPrimeConstants::kModulus) in non-Montogomery representation. 110 | */ 111 | ValueType ToStandardForm() const { return MontgomeryMul(value_, ValueType::One()); } 112 | 113 | std::string ToString() const { return ToStandardForm().ToString(); } 114 | 115 | private: 116 | explicit constexpr PrimeFieldElement(ValueType val) : value_(val) {} 117 | 118 | static constexpr ValueType MontgomeryMul(const ValueType& x, const ValueType& y) { 119 | return ValueType::MontMul(x, y, kModulus, kMontgomeryMPrime); 120 | } 121 | 122 | ValueType value_; 123 | }; 124 | 125 | inline std::ostream& operator<<(std::ostream& out, const PrimeFieldElement& element) { 126 | return out << element.ToString(); 127 | } 128 | 129 | } // namespace starkware 130 | 131 | #endif // STARKWARE_ALGEBRA_PRIME_FIELD_ELEMENT_H_ 132 | -------------------------------------------------------------------------------- /src/starkware/algebra/prime_field_element_test.cc: -------------------------------------------------------------------------------- 1 | #include "starkware/algebra/prime_field_element.h" 2 | 3 | #include "gtest/gtest.h" 4 | 5 | namespace starkware { 6 | namespace { 7 | 8 | TEST(PrimeFieldElementTest, One) { 9 | auto a = PrimeFieldElement::One(); 10 | EXPECT_EQ(a * a, a); 11 | } 12 | 13 | TEST(PrimeFieldElementTest, OneNegTest) { 14 | auto a = PrimeFieldElement::One(); 15 | EXPECT_NE(a * a, a + a); 16 | } 17 | 18 | TEST(PrimeFieldElementTest, AddOneToMinusOne) { 19 | auto a = PrimeFieldElement::One(); 20 | auto z = PrimeFieldElement::Zero(); 21 | auto b = z - a; 22 | EXPECT_EQ(a + b, z); 23 | } 24 | 25 | TEST(PrimeFieldElementTest, AddSimple) { 26 | static_assert(PrimeFieldElement::ValueType::LimbCount() >= 4); 27 | const auto a = PrimeFieldElement::FromBigInt( 28 | 0x01f5883e65f820d099915c908686b9d1c903896a679f32d65369cbe3b0000005_Z); 29 | const auto b = PrimeFieldElement::FromBigInt( 30 | 0x010644e72e131a0f9b8504b68181585d94816a916871ca8d3c208c16d87cfd42_Z); 31 | const auto z = PrimeFieldElement::FromBigInt( 32 | 0x02fbcd25940b3ae0351661470808122f5d84f3fbd010fd638f8a57fa887cfd47_Z); 33 | EXPECT_EQ(a + b, z); 34 | } 35 | 36 | TEST(PrimeField, Random) { 37 | Prng prng; 38 | for (size_t i = 0; i < 100; ++i) { 39 | auto a = PrimeFieldElement::RandomElement(&prng); 40 | auto b = PrimeFieldElement::RandomElement(&prng); 41 | EXPECT_NE(a, b); 42 | } 43 | } 44 | 45 | TEST(PrimeFieldElementTest, Inv) { 46 | auto a = PrimeFieldElement::One(); 47 | auto z = PrimeFieldElement::Zero(); 48 | auto b = z - a; 49 | 50 | EXPECT_EQ(b.Inverse(), b); 51 | } 52 | 53 | TEST(PrimeFieldElementTest, RandomInv) { 54 | Prng prng; 55 | const auto orig = PrimeFieldElement::RandomElement(&prng); 56 | EXPECT_EQ(orig * orig.Inverse(), PrimeFieldElement::One()); 57 | } 58 | 59 | TEST(PrimeFieldElementTest, InvBenchmark) { 60 | Prng prng; 61 | const auto orig = PrimeFieldElement::RandomElement(&prng); 62 | auto val = orig; 63 | 64 | for (size_t i = 0; i < 10001; i++) { 65 | val = val.Inverse(); 66 | } 67 | EXPECT_EQ(val, orig.Inverse()); 68 | } 69 | 70 | PrimeFieldElement NaivePow(const PrimeFieldElement& base, uint64_t exponent) { 71 | auto res = PrimeFieldElement::One(); 72 | for (size_t i = 0; i < exponent; i++) { 73 | res = res * base; 74 | } 75 | return res; 76 | } 77 | 78 | TEST(PrimeFieldElementTest, Pow) { 79 | Prng prng; 80 | const auto base = PrimeFieldElement::RandomElement(&prng); 81 | 82 | EXPECT_EQ(base.Pow(0), PrimeFieldElement::One()); 83 | EXPECT_EQ(base.Pow({}), PrimeFieldElement::One()); 84 | 85 | EXPECT_EQ(base.Pow(1), base); 86 | EXPECT_EQ(base.Pow(std::vector{1}), base); 87 | 88 | const auto expected = NaivePow(base, 100); 89 | EXPECT_EQ(base.Pow(100), expected); 90 | EXPECT_EQ(base.Pow({0, 0, 1, 0, 0, 1, 1}), expected); 91 | } 92 | 93 | TEST(PrimeFieldElementTest, PowRandom) { 94 | Prng prng; 95 | const auto base = PrimeFieldElement::RandomElement(&prng); 96 | const uint64_t exponent = prng.RandomUint64(0, 100); 97 | EXPECT_EQ(base.Pow(exponent), NaivePow(base, exponent)); 98 | } 99 | 100 | } // namespace 101 | } // namespace starkware 102 | -------------------------------------------------------------------------------- /src/starkware/crypto/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(ffi) 2 | 3 | add_library(crypto elliptic_curve_constants.cc pedersen_hash.cc ecdsa.cc) 4 | target_link_libraries(crypto algebra) 5 | 6 | add_executable(elliptic_curve_constants_test elliptic_curve_constants_test.cc) 7 | target_link_libraries(elliptic_curve_constants_test gtest crypto gtest_main pthread) 8 | add_test(elliptic_curve_constants_test elliptic_curve_constants_test) 9 | 10 | add_executable(pedersen_hash_test pedersen_hash_test.cc) 11 | target_link_libraries(pedersen_hash_test crypto gtest gtest_main pthread) 12 | add_test(pedersen_hash_test pedersen_hash_test) 13 | 14 | add_executable(ecdsa_test ecdsa_test.cc) 15 | target_link_libraries(ecdsa_test crypto gtest gtest_main pthread) 16 | add_test(ecdsa_test ecdsa_test) 17 | -------------------------------------------------------------------------------- /src/starkware/crypto/ecdsa.cc: -------------------------------------------------------------------------------- 1 | #include "starkware/crypto/ecdsa.h" 2 | 3 | #include "starkware/algebra/fraction_field_element.h" 4 | #include "starkware/crypto/elliptic_curve_constants.h" 5 | #include "starkware/utils/error_handling.h" 6 | #include "starkware/utils/prng.h" 7 | 8 | namespace starkware { 9 | 10 | EcPoint GetPublicKey(const PrimeFieldElement::ValueType& private_key) { 11 | const auto& generator = GetEcConstants().k_points[1]; 12 | const auto& alpha = GetEcConstants().k_alpha; 13 | return generator.MultiplyByScalar(private_key, alpha); 14 | } 15 | 16 | Signature SignEcdsa( 17 | const PrimeFieldElement::ValueType& private_key, const PrimeFieldElement& z, 18 | const PrimeFieldElement::ValueType& k) { 19 | using ValueType = typename PrimeFieldElement::ValueType; 20 | const auto& generator = GetEcConstants().k_points[1]; 21 | const auto& alpha = GetEcConstants().k_alpha; 22 | const auto& curve_order = GetEcConstants().k_order; 23 | constexpr auto upper_bound = 0x800000000000000000000000000000000000000000000000000000000000000_Z; 24 | static_assert(upper_bound <= PrimeFieldElement::kModulus); 25 | ASSERT(upper_bound <= curve_order, "Unexpected curve size."); 26 | 27 | ASSERT(z != PrimeFieldElement::Zero(), "Message cannot be zero."); 28 | ASSERT(z.ToStandardForm() < upper_bound, "z is too big."); 29 | ASSERT(k != ValueType::Zero(), "k must not be zero"); 30 | 31 | const PrimeFieldElement x = generator.MultiplyByScalar(k, alpha).x; 32 | const ValueType r = x.ToStandardForm(); 33 | ASSERT( 34 | (r < curve_order) && (r != ValueType::Zero()), 35 | "Bad randomness, please try a different a different k."); 36 | 37 | const ValueType k_inv = k.InvModPrime(curve_order); 38 | ValueType s = ValueType::MulMod(r, private_key, curve_order); 39 | // Non modular addition, requires the summands to be small enough to prevent overflow. 40 | ASSERT(curve_order.NumLeadingZeros() > 0, "Implementation assumes smaller curve."); 41 | s = s + z.ToStandardForm(); 42 | s = ValueType::MulMod(s, k_inv, curve_order); 43 | ASSERT(s != ValueType::Zero(), "Bad randomness, please try a different k."); 44 | 45 | const ValueType w = s.InvModPrime(curve_order); 46 | ASSERT(w < upper_bound, "Bad randomness, please try a different k."); 47 | const PrimeFieldElement w_field = PrimeFieldElement::FromBigInt(w); 48 | return {x, w_field}; 49 | } 50 | 51 | bool VerifyEcdsa( 52 | const EcPoint& public_key, const PrimeFieldElement& z, 53 | const Signature& sig) { 54 | using FractionFieldElementT = FractionFieldElement; 55 | using EcPointT = EcPoint; 56 | const auto& r = sig.first; 57 | const auto& w = sig.second; 58 | // z, r, w should be smaller than 2^251. 59 | const auto upper_bound = 0x800000000000000000000000000000000000000000000000000000000000000_Z; 60 | ASSERT(z != PrimeFieldElement::Zero(), "Message cannot be zero."); 61 | ASSERT(z.ToStandardForm() < upper_bound, "z is too big."); 62 | ASSERT(r != PrimeFieldElement::Zero(), "r cannot be zero."); 63 | ASSERT(r.ToStandardForm() < upper_bound, "r is too big."); 64 | ASSERT(w != PrimeFieldElement::Zero(), "w cannot be zero."); 65 | ASSERT(w.ToStandardForm() < upper_bound, "w is too big."); 66 | const FractionFieldElementT alpha(GetEcConstants().k_alpha); 67 | const auto generator = GetEcConstants().k_points[1]; 68 | const auto zw = PrimeFieldElement::ValueType::MulMod( 69 | z.ToStandardForm(), w.ToStandardForm(), GetEcConstants().k_order); 70 | const EcPointT zw_g = generator.ConvertTo().MultiplyByScalar(zw, alpha); 71 | const auto rw = PrimeFieldElement::ValueType::MulMod( 72 | r.ToStandardForm(), w.ToStandardForm(), GetEcConstants().k_order); 73 | const EcPointT rw_q = public_key.ConvertTo().MultiplyByScalar(rw, alpha); 74 | return (zw_g + rw_q).x.ToBaseFieldElement() == r || (zw_g - rw_q).x.ToBaseFieldElement() == r; 75 | } 76 | 77 | bool VerifyEcdsaPartialKey( 78 | const PrimeFieldElement& public_key_x, const PrimeFieldElement& z, const Signature& sig) { 79 | const auto alpha = GetEcConstants().k_alpha; 80 | const auto beta = GetEcConstants().k_beta; 81 | const auto public_key = EcPoint::GetPointFromX(public_key_x, alpha, beta); 82 | ASSERT( 83 | public_key.has_value(), "Given public key (" + public_key_x.ToString() + 84 | ") does not correspond to a valid point on the elliptic curve."); 85 | 86 | // There are two points on the elliptic curve with the given public_key_x, both will be 87 | // tested by VerifyEcdsa(). 88 | return VerifyEcdsa(*public_key, z, sig); 89 | } 90 | 91 | } // namespace starkware 92 | -------------------------------------------------------------------------------- /src/starkware/crypto/ecdsa.h: -------------------------------------------------------------------------------- 1 | #ifndef STARKWARE_CRYPTO_ECDSA_H_ 2 | #define STARKWARE_CRYPTO_ECDSA_H_ 3 | 4 | #include 5 | 6 | #include "starkware/algebra/elliptic_curve.h" 7 | #include "starkware/algebra/prime_field_element.h" 8 | 9 | namespace starkware { 10 | 11 | /* 12 | Type representing a signature (r, w). 13 | */ 14 | using Signature = std::pair; 15 | 16 | /* 17 | Deduces the public key given a private key. 18 | The x coordinate of the public key is also known as the partial public key, 19 | and used in StarkEx to identify the user. 20 | */ 21 | EcPoint GetPublicKey(const PrimeFieldElement::ValueType& private_key); 22 | 23 | /* 24 | Signs message hash z with the provided private_key, with randomness k. 25 | 26 | NOTE: k should be a strong cryptographical random, and not repeat. 27 | See: https://tools.ietf.org/html/rfc6979. 28 | */ 29 | Signature SignEcdsa( 30 | const PrimeFieldElement::ValueType& private_key, const PrimeFieldElement& z, 31 | const PrimeFieldElement::ValueType& k); 32 | 33 | /* 34 | Verifies ECDSA signature of a given hash message z with a given public key. 35 | Returns true if either public_key or -public_key signs the message. 36 | NOTE: This function assumes that the public_key is on the curve. 37 | */ 38 | bool VerifyEcdsa( 39 | const EcPoint& public_key, const PrimeFieldElement& z, const Signature& sig); 40 | 41 | /* 42 | Same as VerifyEcdsa() except that only the x coordinate of the public key is given. The y 43 | coordinate is computed, and both options are checked. 44 | */ 45 | bool VerifyEcdsaPartialKey( 46 | const PrimeFieldElement& public_key_x, const PrimeFieldElement& z, const Signature& sig); 47 | 48 | } // namespace starkware 49 | 50 | #endif // STARKWARE_CRYPTO_ECDSA_H_ 51 | -------------------------------------------------------------------------------- /src/starkware/crypto/ecdsa_test.cc: -------------------------------------------------------------------------------- 1 | #include "starkware/crypto/ecdsa.h" 2 | 3 | #include "gtest/gtest.h" 4 | 5 | #include "starkware/algebra/elliptic_curve.h" 6 | #include "starkware/algebra/prime_field_element.h" 7 | #include "starkware/crypto/elliptic_curve_constants.h" 8 | 9 | namespace starkware { 10 | namespace { 11 | 12 | TEST(ECDSA, PublicKeyFromPrivate) { 13 | const auto private_key = 0x3c1e9550e66958296d11b60f8e8e7a7ad990d07fa65d5f7652c4a6c87d4e3cc_Z; 14 | const EcPoint public_key( 15 | PrimeFieldElement::FromBigInt( 16 | 0x77a3b314db07c45076d11f62b6f9e748a39790441823307743cf00d6597ea43_Z), 17 | PrimeFieldElement::FromBigInt( 18 | 0x54d7beec5ec728223671c627557efc5c9a6508425dc6c900b7741bf60afec06_Z)); 19 | 20 | EXPECT_EQ(public_key, GetPublicKey(private_key)); 21 | } 22 | 23 | TEST(ECDSA, SignAndVerify) { 24 | Prng prng; 25 | using ValueType = PrimeFieldElement::ValueType; 26 | 27 | // Draw test parameters. 28 | const auto private_key = ValueType::RandomBigInt(&prng); 29 | const auto public_key = GetPublicKey(private_key); 30 | const auto msg = PrimeFieldElement::RandomElement(&prng); 31 | // Choose an arbitrary value for k, 32 | const auto k = 0x54d7beec5ec728223671c627557efc5c9a6508425dc6c900b7741bf60afec06_Z; 33 | 34 | const auto signature = SignEcdsa(private_key, msg, k); 35 | EXPECT_TRUE(VerifyEcdsa(public_key, msg, signature)); 36 | } 37 | 38 | TEST(VerifyEcdsa, Regression) { 39 | Prng prng; 40 | const auto alpha = GetEcConstants().k_alpha; 41 | const auto beta = GetEcConstants().k_beta; 42 | 43 | // The following hard-coded values were cross-checked with a number of implementations of the 44 | // ECDSA verification algorithm. 45 | const auto public_key_x = PrimeFieldElement::FromBigInt( 46 | 0x77a3b314db07c45076d11f62b6f9e748a39790441823307743cf00d6597ea43_Z); 47 | const auto public_key_y = PrimeFieldElement::FromBigInt( 48 | 0x54d7beec5ec728223671c627557efc5c9a6508425dc6c900b7741bf60afec06_Z); 49 | const EcPoint public_key(public_key_x, public_key_y); 50 | const EcPoint wrong_public_key = 51 | EcPoint::Random(alpha, beta, &prng); 52 | 53 | const auto z = PrimeFieldElement::FromBigInt( 54 | 0x397e76d1667c4454bfb83514e120583af836f8e32a516765497823eabe16a3f_Z); 55 | const auto r = PrimeFieldElement::FromBigInt( 56 | 0x173fd03d8b008ee7432977ac27d1e9d1a1f6c98b1a2f05fa84a21c84c44e882_Z); 57 | const auto w = PrimeFieldElement::FromBigInt( 58 | 0x1f2c44a7798f55192f153b4c48ea5c1241fbb69e6132cc8a0da9c5b62a4286e_Z); 59 | 60 | EXPECT_TRUE(VerifyEcdsa(public_key, z, {r, w})); 61 | EXPECT_TRUE(VerifyEcdsa(-public_key, z, {r, w})); 62 | EXPECT_FALSE(VerifyEcdsa(wrong_public_key, z, {r, w})); 63 | EXPECT_FALSE(VerifyEcdsa(public_key, z + PrimeFieldElement::One(), {r, w})); 64 | EXPECT_FALSE(VerifyEcdsa(public_key, z, {r + PrimeFieldElement::One(), w})); 65 | EXPECT_FALSE(VerifyEcdsa(public_key, z, {r, w + PrimeFieldElement::One()})); 66 | 67 | EXPECT_TRUE(VerifyEcdsaPartialKey(public_key_x, z, {r, w})); 68 | EXPECT_FALSE(VerifyEcdsaPartialKey(wrong_public_key.x, z, {r, w})); 69 | EXPECT_FALSE(VerifyEcdsaPartialKey(public_key_x, z + PrimeFieldElement::One(), {r, w})); 70 | EXPECT_FALSE(VerifyEcdsaPartialKey(public_key_x, z, {r + PrimeFieldElement::One(), w})); 71 | EXPECT_FALSE(VerifyEcdsaPartialKey(public_key_x, z, {r, w + PrimeFieldElement::One()})); 72 | EXPECT_FALSE(VerifyEcdsaPartialKey(public_key_y, z, {r, w})); 73 | } 74 | 75 | TEST(VerifyEcdsa, Benchmark) { 76 | Prng prng; 77 | for (size_t i = 0; i < 100; i++) { 78 | EXPECT_FALSE(VerifyEcdsa( 79 | {PrimeFieldElement::RandomElement(&prng), PrimeFieldElement::RandomElement(&prng)}, 80 | PrimeFieldElement::RandomElement(&prng), 81 | {PrimeFieldElement::RandomElement(&prng), PrimeFieldElement::RandomElement(&prng)})); 82 | } 83 | } 84 | 85 | TEST(VerifyEcdsaPartialKey, Benchmark) { 86 | Prng prng; 87 | const auto public_key_x = PrimeFieldElement::FromBigInt( 88 | 0x77a3b314db07c45076d11f62b6f9e748a39790441823307743cf00d6597ea43_Z); 89 | for (size_t i = 0; i < 100; i++) { 90 | EXPECT_FALSE(VerifyEcdsaPartialKey( 91 | public_key_x, PrimeFieldElement::RandomElement(&prng), 92 | {PrimeFieldElement::RandomElement(&prng), PrimeFieldElement::RandomElement(&prng)})); 93 | } 94 | } 95 | 96 | } // namespace 97 | } // namespace starkware 98 | -------------------------------------------------------------------------------- /src/starkware/crypto/elliptic_curve_constants.h: -------------------------------------------------------------------------------- 1 | #ifndef STARKWARE_CRYPTO_ELLIPTIC_CURVE_CONSTANTS_H_ 2 | #define STARKWARE_CRYPTO_ELLIPTIC_CURVE_CONSTANTS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "starkware/algebra/big_int.h" 9 | #include "starkware/algebra/elliptic_curve.h" 10 | #include "starkware/algebra/prime_field_element.h" 11 | 12 | namespace starkware { 13 | 14 | /* 15 | Contains a set of constants that go along with an elliptic curve. 16 | 17 | FieldElementT is the underlying field of the curve. 18 | The equation of the elliptic curve is y^2 = x^3 + k_alpha * x + k_beta. 19 | k_order is the size of the group. 20 | k_points are points on the curve that were generated independently in a "nothing up my sleeve" 21 | manner to ensure that no one knows their discrete log. 22 | */ 23 | template 24 | struct EllipticCurveConstants { 25 | public: 26 | using ValueType = typename FieldElementT::ValueType; 27 | 28 | const FieldElementT k_alpha; 29 | const FieldElementT k_beta; 30 | const ValueType k_order; 31 | const std::vector> k_points; 32 | 33 | constexpr EllipticCurveConstants( 34 | const FieldElementT& k_alpha, const FieldElementT& k_beta, const ValueType& k_order, 35 | std::vector> k_points) noexcept 36 | : k_alpha(k_alpha), k_beta(k_beta), k_order(k_order), k_points(std::move(k_points)) {} 37 | 38 | constexpr EllipticCurveConstants( 39 | const ValueType& k_alpha, const ValueType& k_beta, const ValueType& k_order, 40 | std::initializer_list> k_points) noexcept 41 | : EllipticCurveConstants( 42 | FieldElementT::FromBigInt(k_alpha), FieldElementT::FromBigInt(k_beta), k_order, 43 | ECPointsVectorFromPairs(std::move(k_points))) {} 44 | 45 | private: 46 | static std::vector> ECPointsVectorFromPairs( 47 | std::initializer_list> k_points) { 48 | std::vector> res; 49 | res.reserve(k_points.size()); 50 | 51 | for (const auto& p : k_points) { 52 | res.emplace_back(FieldElementT::FromBigInt(p.first), FieldElementT::FromBigInt(p.second)); 53 | } 54 | return res; 55 | } 56 | }; 57 | 58 | /* 59 | This elliptic curve over the prime field PrimeFieldElement was chosen in a "nothing up my sleeve" 60 | manner to show that we don't know any special properties of this curve (other than being of prime 61 | order). 62 | 63 | alpha was chosen to be 1 because any elliptic curve has an isomorphic curve with a small alpha, 64 | but we didn't want a zero alpha because then the discriminant is small. 65 | 66 | beta was generated in the following way: 67 | 1) Take beta to be the integer whose digits are the first 76 decimal digits of pi (76 is the 68 | number of digits required to represent a field element). 69 | 2) While [y^2 = x^3 + alpha * x + beta] is not a curve of prime order, increase beta by 1. 70 | 71 | The points were generated by the following steps: 72 | 1) Take the decimal digits of pi and split them into chunks of 76 digits (the number of decimal 73 | digits of the modulus). 74 | 75 | 2) Each chunk of 76 digits is the seed for generating a point, except for the first chunk 76 | which was used for generating the curve. 77 | 78 | 3) For each such seed x: 79 | 80 | 3.1) while (x^3 + alpha * x + beta) is not a square in the prime field: 81 | increase x by 1. 82 | 83 | 3.2) (x, square_root(x^3 + alpha * x + beta)) is a point on the elliptic curve (for square_root 84 | the smaller root). 85 | 86 | 87 | 4) The first two points are taken as-is, as they will be used as the shift point and the 88 | ECDSA generator point. 89 | 90 | 5) Each subsequent point P is expanded to 248 or 4 points alternatingly, by taking the set 91 | {2^i P : 0 <= i < 248} or {2^i P : 0 <= i < 3}. 248 is chosen to be the largest multiple of 8 92 | lower than 251. 93 | 94 | This is a sage code that implements these steps: 95 | 96 | R = RealField(400000) 97 | long_pi_string = '3' + str(R(pi))[2:] 98 | p = 2^251 + 17 * 2^192 + 1 99 | beta = GF(p)(long_pi_string[:76]) + 379 100 | ec = EllipticCurve(GF(p), [1, beta]) 101 | points = [] 102 | for i in range(1, 13): 103 | x = GF(p)(int(long_pi_string[i * 76 : (i+1) * 76])) 104 | while not is_square(x^3 + x + beta): 105 | x += 1 106 | P = ec((x, sqrt(x^3 + x + beta))) 107 | if i <= 2: 108 | points.append(P.xy()) 109 | continue 110 | for j in range(248 if i%2==1 else 4): 111 | points.append(P.xy()) 112 | P *= 2 113 | print "".join("{0x%x_Z,0x%x_Z},\n" % p for p in points) 114 | */ 115 | const EllipticCurveConstants& GetEcConstants(); 116 | 117 | } // namespace starkware 118 | 119 | #endif // STARKWARE_CRYPTO_ELLIPTIC_CURVE_CONSTANTS_H_ 120 | -------------------------------------------------------------------------------- /src/starkware/crypto/elliptic_curve_constants_test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "gtest/gtest.h" 4 | 5 | #include "starkware/algebra/prime_field_element.h" 6 | #include "starkware/crypto/elliptic_curve_constants.h" 7 | #include "starkware/utils/prng.h" 8 | #include "starkware/utils/test_utils.h" 9 | 10 | namespace starkware { 11 | namespace { 12 | 13 | TEST(EllipticCurveConstants, MulByCurveOrderTest) { 14 | Prng prng; 15 | using FieldValueType = EllipticCurveConstants::ValueType; 16 | const EcPoint generator = GetEcConstants().k_points[1]; 17 | const FieldValueType random_power = FieldValueType(prng.RandomUint64(1, 1000)); 18 | // Multiply by the group order (result should be zero). 19 | const auto alpha = GetEcConstants().k_alpha; 20 | const auto order = GetEcConstants().k_order; 21 | EXPECT_ASSERT(generator.MultiplyByScalar(order, alpha), testing::HasSubstr("zero element")); 22 | EXPECT_EQ( 23 | generator.MultiplyByScalar(random_power, alpha), 24 | generator.MultiplyByScalar(order + random_power, alpha)); 25 | } 26 | 27 | } // namespace 28 | } // namespace starkware 29 | -------------------------------------------------------------------------------- /src/starkware/crypto/ffi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(js) 2 | 3 | add_library(crypto_c_exports SHARED pedersen_hash.cc ecdsa.cc utils.cc) 4 | target_link_libraries(crypto_c_exports crypto) 5 | 6 | add_custom_command( 7 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/crypto_lib/crypto_lib.go 8 | COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/crypto_lib/ ${CMAKE_CURRENT_BINARY_DIR}/crypto_lib/ 9 | DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/crypto_lib/crypto_lib.go 10 | ) 11 | 12 | add_custom_command( 13 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/crypto_lib_test.go 14 | COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/crypto_lib_test.go ${CMAKE_CURRENT_BINARY_DIR} 15 | DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/crypto_lib_test.go 16 | ) 17 | 18 | add_custom_command( 19 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ecdsa.h 20 | COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/ecdsa.h ${CMAKE_CURRENT_BINARY_DIR} 21 | DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ecdsa.h 22 | ) 23 | 24 | add_custom_command( 25 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/pedersen_hash.h 26 | COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/pedersen_hash.h ${CMAKE_CURRENT_BINARY_DIR} 27 | DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/pedersen_hash.h 28 | ) 29 | 30 | add_custom_target( 31 | CopyGoFiles ALL 32 | DEPENDS 33 | ${CMAKE_CURRENT_BINARY_DIR}/crypto_lib/crypto_lib.go 34 | ${CMAKE_CURRENT_BINARY_DIR}/crypto_lib_test.go 35 | ${CMAKE_CURRENT_BINARY_DIR}/ecdsa.h 36 | ${CMAKE_CURRENT_BINARY_DIR}/pedersen_hash.h 37 | ) 38 | -------------------------------------------------------------------------------- /src/starkware/crypto/ffi/crypto_lib/crypto_lib.go: -------------------------------------------------------------------------------- 1 | package crypto_lib 2 | 3 | import "encoding/hex" 4 | import ( 5 | "fmt" 6 | "math/big" 7 | ) 8 | /* 9 | 10 | #cgo CFLAGS: -I. 11 | #cgo LDFLAGS: -L./.. -lcrypto_c_exports -Wl,-rpath=./. 12 | #include 13 | #include "../ecdsa.h" 14 | #include "../pedersen_hash.h" 15 | 16 | */ 17 | import "C" 18 | import "unsafe" 19 | 20 | 21 | const curveOrder = "800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f" 22 | 23 | /* 24 | Computes the component s of a signature given the component w. 25 | */ 26 | func invertOnCurve(w string) string { 27 | w_big_int := new(big.Int) 28 | w_big_int.SetString(w[2:], 16) 29 | order := new(big.Int) 30 | order.SetString(curveOrder, 16) 31 | w_big_int.ModInverse(w_big_int, order) 32 | s := fmt.Sprintf("0x0%x", w_big_int) 33 | return s 34 | } 35 | 36 | /* 37 | Given a hex string reverses its endian represenation. 38 | */ 39 | func reverseHexEndianRepresentation(s string) string { 40 | rns := []rune(s) 41 | for i, j := 0, len(rns)-2; i < j; i, j = i+2, j-2 { 42 | rns[i], rns[j] = rns[j], rns[i] 43 | rns[i+1], rns[j+1] = rns[j+1], rns[i+1] 44 | } 45 | return string(rns) 46 | } 47 | 48 | /* 49 | Pads the given hex string with leading zeroes so that its length is 64 characters (32 bytes). 50 | */ 51 | func padHexString(s string) string { 52 | padded := fmt.Sprintf("0x%064s", s[2:]) 53 | return padded 54 | } 55 | 56 | /* 57 | Computes the StarkWare version of the Pedersen hash of x and y. 58 | Full specification of the hash function can be found here: 59 | https://docs.starkware.co/starkex-docs/crypto/pedersen-hash-function 60 | */ 61 | func Hash(input1, input2 string) string { 62 | input1_dec, _ := hex.DecodeString(reverseHexEndianRepresentation(input1)) 63 | input2_dec, _ := hex.DecodeString(reverseHexEndianRepresentation(input2)) 64 | in1 := C.CBytes(input1_dec) 65 | in2 := C.CBytes(input2_dec) 66 | var o [1024]byte 67 | out := C.CBytes(o[:]) 68 | 69 | res := C.Hash( 70 | (*C.char)(unsafe.Pointer(in1)), 71 | (*C.char)(unsafe.Pointer(in2)), 72 | (*C.char)(unsafe.Pointer(out))) 73 | 74 | if res != 0 { 75 | fmt.Printf("Pedersen hash encountered an error: %s\n", C.GoBytes(unsafe.Pointer(out), 1024)) 76 | C.free(unsafe.Pointer(in1)) 77 | C.free(unsafe.Pointer(in2)) 78 | C.free(unsafe.Pointer(out)) 79 | return "" 80 | } 81 | 82 | hash_result := "0x" + reverseHexEndianRepresentation( 83 | hex.EncodeToString(C.GoBytes(unsafe.Pointer(out), 32))) 84 | 85 | C.free(unsafe.Pointer(in1)) 86 | C.free(unsafe.Pointer(in2)) 87 | C.free(unsafe.Pointer(out)) 88 | 89 | return hash_result 90 | } 91 | 92 | /* 93 | Deduces the public key given a private key. 94 | */ 95 | func GetPublicKey(private_key string) string { 96 | private_key_dec, _ := hex.DecodeString( 97 | reverseHexEndianRepresentation(padHexString(private_key))) 98 | private_key_bytes := C.CBytes(private_key_dec) 99 | var o [1024]byte 100 | out := C.CBytes(o[:]) 101 | 102 | res := C.GetPublicKey( 103 | (*C.char)(unsafe.Pointer(private_key_bytes)), (*C.char)(unsafe.Pointer(out))) 104 | 105 | if res != 0 { 106 | fmt.Printf("GetPublicKey encountered an error: %s\n", C.GoBytes(unsafe.Pointer(out), 1024)) 107 | C.free(unsafe.Pointer(private_key_bytes)) 108 | C.free(unsafe.Pointer(out)) 109 | return "" 110 | } 111 | 112 | public_key_result := "0x" + reverseHexEndianRepresentation( 113 | hex.EncodeToString(C.GoBytes(unsafe.Pointer(out), 32))) 114 | 115 | C.free(unsafe.Pointer(private_key_bytes)) 116 | C.free(unsafe.Pointer(out)) 117 | 118 | return public_key_result 119 | } 120 | 121 | /* 122 | Verifies ECDSA signature of a given message hash z with a given public key. 123 | Returns true if public_key signs the message. 124 | NOTE: This function assumes that the public_key is on the curve. 125 | */ 126 | func Verify(stark_key, msg_hash, r, s string) bool { 127 | stark_key_dec, _ := hex.DecodeString(reverseHexEndianRepresentation(padHexString(stark_key))) 128 | stark_key_bytes := C.CBytes(stark_key_dec) 129 | 130 | message_dec, _ := hex.DecodeString(reverseHexEndianRepresentation(padHexString(msg_hash))) 131 | message_bytes := C.CBytes(message_dec) 132 | 133 | r_dec, _ := hex.DecodeString(reverseHexEndianRepresentation(padHexString(r))) 134 | r_bytes := C.CBytes(r_dec) 135 | 136 | w := invertOnCurve(s) 137 | w_dec, _ := hex.DecodeString(reverseHexEndianRepresentation(padHexString(w))) 138 | w_bytes := C.CBytes(w_dec) 139 | 140 | res := C.Verify( 141 | (*C.char)(unsafe.Pointer(stark_key_bytes)), 142 | (*C.char)(unsafe.Pointer(message_bytes)), 143 | (*C.char)(unsafe.Pointer(r_bytes)), 144 | (*C.char)(unsafe.Pointer(w_bytes))) 145 | 146 | C.free(unsafe.Pointer(stark_key_bytes)) 147 | C.free(unsafe.Pointer(message_bytes)) 148 | C.free(unsafe.Pointer(r_bytes)) 149 | C.free(unsafe.Pointer(w_bytes)) 150 | 151 | if res == 0 { 152 | return false 153 | } 154 | return true 155 | } 156 | 157 | /* 158 | Signs the given message hash with the provided private_key, with randomness k. 159 | 160 | NOTE: k should be a strong cryptographical random, and not repeat. 161 | See: https://tools.ietf.org/html/rfc6979. 162 | */ 163 | func Sign(private_key, message, k string) (string, string) { 164 | private_key_dec, _ := hex.DecodeString( 165 | reverseHexEndianRepresentation(padHexString(private_key))) 166 | private_key_bytes := C.CBytes(private_key_dec) 167 | 168 | message_dec, _ := hex.DecodeString(reverseHexEndianRepresentation(padHexString(message))) 169 | message_bytes := C.CBytes(message_dec) 170 | 171 | k_dec, _ := hex.DecodeString(reverseHexEndianRepresentation(padHexString(k))) 172 | k_bytes := C.CBytes(k_dec) 173 | 174 | var o [1024]byte 175 | out := C.CBytes(o[:]) 176 | 177 | C.Sign( 178 | (*C.char)(unsafe.Pointer(private_key_bytes)), 179 | (*C.char)(unsafe.Pointer(message_bytes)), 180 | (*C.char)(unsafe.Pointer(k_bytes)), 181 | (*C.char)(unsafe.Pointer(out))) 182 | 183 | res := reverseHexEndianRepresentation(hex.EncodeToString(C.GoBytes(unsafe.Pointer(out), 64))) 184 | signature_w := "0x" + res[0:64] 185 | signature_r := "0x" + res[64:] 186 | 187 | signature_s := invertOnCurve(signature_w) 188 | 189 | C.free(unsafe.Pointer(private_key_bytes)) 190 | C.free(unsafe.Pointer(message_bytes)) 191 | C.free(unsafe.Pointer(k_bytes)) 192 | C.free(unsafe.Pointer(out)) 193 | 194 | return signature_r, signature_s 195 | } 196 | -------------------------------------------------------------------------------- /src/starkware/crypto/ffi/crypto_lib_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "./crypto_lib" 4 | import "testing" 5 | 6 | func TestHash(t *testing.T) { 7 | res := crypto_lib.Hash( 8 | "0x03d937c035c878245caf64531a5756109c53068da139362728feb561405371cb", 9 | "0x0208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a") 10 | 11 | expected_hash := "0x030e480bed5fe53fa909cc0f8c4d99b8f9f2c016be4c41e13a4848797979c662" 12 | if res != expected_hash { 13 | t.Errorf("Hash error: expected %s but got %s.", expected_hash, res) 14 | } 15 | } 16 | 17 | func TestGetPublicKey(t *testing.T) { 18 | res := crypto_lib.GetPublicKey( 19 | "0x03c1e9550e66958296d11b60f8e8e7a7ad990d07fa65d5f7652c4a6c87d4e3cc") 20 | 21 | expected_key := "0x077a3b314db07c45076d11f62b6f9e748a39790441823307743cf00d6597ea43" 22 | if res != expected_key { 23 | t.Errorf("GetPublicKey error: expected %s but got %s.", expected_key, res) 24 | } 25 | 26 | res = crypto_lib.GetPublicKey("0x12") 27 | 28 | expected_key = "0x019661066e96a8b9f06a1d136881ee924dfb6a885239caa5fd3f87a54c6b25c4" 29 | if res != expected_key { 30 | t.Errorf("GetPublicKey error: expected %s but got %s.", expected_key, res) 31 | } 32 | } 33 | 34 | func TestVerify(t *testing.T) { 35 | res := crypto_lib.Verify( 36 | "0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca", 37 | "0x2", 38 | "0x411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20", 39 | "0x405c3191ab3883ef2b763af35bc5f5d15b3b4e99461d70e84c654a351a7c81b") 40 | if !(res) { 41 | t.Errorf("Verify error: valid message was not verified correctly.") 42 | } 43 | 44 | res = crypto_lib.Verify( 45 | "0x077a4b314db07c45076d11f62b6f9e748a39790441823307743cf00d6597ea43", 46 | "0x0397e76d1667c4454bfb83514e120583af836f8e32a516765497823eabe16a3f", 47 | "0x0173fd03d8b008ee7432977ac27d1e9d1a1f6c98b1a2f05fa84a21c84c44e882", 48 | "0x01f2c44a7798f55192f153b4c48ea5c1241fbb69e6132cc8a0da9c5b62a4286e") 49 | if (res) { 50 | t.Errorf("Verify error: invalid message was not rejected.") 51 | } 52 | } 53 | 54 | func TestSign(t *testing.T) { 55 | r, s := crypto_lib.Sign("0x1", "0x2", "0x3") 56 | public_key := crypto_lib.GetPublicKey("0x1") 57 | res := crypto_lib.Verify(public_key, "0x2", r, s) 58 | if !(res) { 59 | t.Errorf("Sign error: signature rejected by verification.") 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/starkware/crypto/ffi/ecdsa.cc: -------------------------------------------------------------------------------- 1 | #include "starkware/crypto/ffi/ecdsa.h" 2 | #include "starkware/crypto/ecdsa.h" 3 | 4 | #include 5 | 6 | #include "third_party/gsl/gsl-lite.hpp" 7 | 8 | #include "starkware/algebra/prime_field_element.h" 9 | #include "starkware/crypto/ffi/utils.h" 10 | 11 | namespace starkware { 12 | 13 | namespace { 14 | 15 | using ValueType = PrimeFieldElement::ValueType; 16 | 17 | constexpr size_t kElementSize = sizeof(ValueType); 18 | constexpr size_t kOutBufferSize = 1024; 19 | static_assert(kOutBufferSize >= kElementSize, "kOutBufferSize is not big enough"); 20 | 21 | } // namespace 22 | 23 | extern "C" int GetPublicKey( 24 | const gsl::byte private_key[kElementSize], gsl::byte out[kElementSize]) { 25 | try { 26 | const auto stark_key = GetPublicKey(Deserialize(gsl::make_span(private_key, kElementSize))).x; 27 | Serialize(stark_key.ToStandardForm(), gsl::make_span(out, kElementSize)); 28 | } catch (const std::exception& e) { 29 | return HandleError(e.what(), gsl::make_span(out, kOutBufferSize)); 30 | } catch (...) { 31 | return HandleError("Unknown c++ exception.", gsl::make_span(out, kOutBufferSize)); 32 | } 33 | return 0; 34 | } 35 | 36 | extern "C" bool Verify( 37 | const gsl::byte stark_key[kElementSize], const gsl::byte msg_hash[kElementSize], 38 | const gsl::byte r_bytes[kElementSize], const gsl::byte w_bytes[kElementSize]) { 39 | try { 40 | // The following call will throw in case of verification failure. 41 | return VerifyEcdsaPartialKey( 42 | PrimeFieldElement::FromBigInt(Deserialize(gsl::make_span(stark_key, kElementSize))), 43 | PrimeFieldElement::FromBigInt(Deserialize(gsl::make_span(msg_hash, kElementSize))), 44 | {PrimeFieldElement::FromBigInt(Deserialize(gsl::make_span(r_bytes, kElementSize))), 45 | PrimeFieldElement::FromBigInt(Deserialize(gsl::make_span(w_bytes, kElementSize)))}); 46 | } catch (...) { 47 | return false; 48 | } 49 | return true; 50 | } 51 | 52 | extern "C" int Sign( 53 | const gsl::byte private_key[kElementSize], const gsl::byte message[kElementSize], 54 | const gsl::byte k[kElementSize], gsl::byte out[kOutBufferSize]) { 55 | try { 56 | const auto sig = SignEcdsa( 57 | Deserialize(gsl::make_span(private_key, kElementSize)), 58 | PrimeFieldElement::FromBigInt(Deserialize(gsl::make_span(message, kElementSize))), 59 | Deserialize(gsl::make_span(k, kElementSize))); 60 | 61 | Serialize(sig.first.ToStandardForm(), gsl::make_span(out, kElementSize)); 62 | Serialize(sig.second.ToStandardForm(), gsl::make_span(out + kElementSize, kElementSize)); 63 | } catch (const std::exception& e) { 64 | return HandleError(e.what(), gsl::make_span(out, kOutBufferSize)); 65 | } catch (...) { 66 | return HandleError("Unknown c++ exception.", gsl::make_span(out, kOutBufferSize)); 67 | } 68 | return 0; 69 | } 70 | 71 | } // namespace starkware 72 | -------------------------------------------------------------------------------- /src/starkware/crypto/ffi/ecdsa.h: -------------------------------------------------------------------------------- 1 | #ifndef STARKWARE_CRYPTO_FFI_ECDSA_H_ 2 | #define STARKWARE_CRYPTO_FFI_ECDSA_H_ 3 | 4 | int GetPublicKey(const char* private_key, char* out); 5 | 6 | int Verify(const char* stark_key, const char* msg_hash, const char* r_bytes, const char* w_bytes); 7 | 8 | int Sign(const char* private_key, const char* message, const char* k, char* out); 9 | 10 | #endif // STARKWARE_CRYPTO_FFI_ECDSA_H_ 11 | -------------------------------------------------------------------------------- /src/starkware/crypto/ffi/js/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_custom_command( 2 | OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/libcrypto_c_exports.so 3 | COMMAND 4 | ${CMAKE_COMMAND} -E copy 5 | ${CMAKE_CURRENT_BINARY_DIR}/../libcrypto_c_exports.so 6 | ${CMAKE_CURRENT_SOURCE_DIR}/libcrypto_c_exports.so 7 | DEPENDS crypto_c_exports 8 | ) 9 | 10 | add_custom_target(js_test ALL 11 | DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libcrypto_c_exports.so 12 | ) 13 | 14 | add_test( 15 | NAME js_test 16 | COMMAND npm test 17 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 18 | ) 19 | -------------------------------------------------------------------------------- /src/starkware/crypto/ffi/js/crypto.js: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////////// 2 | // Copyright 2019 StarkWare Industries Ltd. // 3 | // // 4 | // Licensed under the Apache License, Version 2.0 (the "License"). // 5 | // You may not use this file except in compliance with the License. // 6 | // You may obtain a copy of the License at // 7 | // // 8 | // https://www.starkware.co/open-source-license/ // 9 | // // 10 | // Unless required by applicable law or agreed to in writing, // 11 | // software distributed under the License is distributed on an "AS IS" BASIS, // 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // 13 | // See the License for the specific language governing permissions // 14 | // and limitations under the License. // 15 | ///////////////////////////////////////////////////////////////////////////////// 16 | 17 | const BN = require('bn.js'); 18 | const BigIntBuffer = require('bigint-buffer'); 19 | const { assert } = require('chai'); 20 | const ffi = require('ffi-napi'); 21 | 22 | // Native crypto bindings. 23 | const libcrypto = ffi.Library('./libcrypto_c_exports', { 24 | 'Hash': ['int', ['string', 'string', 'string']], 25 | 'Verify': ['bool', ['string', 'string', 'string', 'string']], 26 | 'Sign': ['int', ['string', 'string', 'string', 'string']], 27 | 'GetPublicKey': ['int', ['string', 'string']], 28 | }); 29 | 30 | const curveOrder = new BN('800000000000010ffffffffffffffffb781126dcae7b2321e66a241adc64d2f', 16) 31 | 32 | /* 33 | Computes the StarkWare version of the Pedersen hash of x and y. 34 | Full specification of the hash function can be found here: 35 | https://docs.starkware.co/starkex-docs/crypto/pedersen-hash-function 36 | */ 37 | function pedersen(x, y) { 38 | const x_buf = BigIntBuffer.toBufferLE(x, 32); 39 | const y_buf = BigIntBuffer.toBufferLE(y, 32); 40 | const res_buf = Buffer.alloc(1024); 41 | const res = libcrypto.Hash(x_buf, y_buf, res_buf); 42 | assert(res == 0, 'Error: ' + res_buf.toString('utf-8')); 43 | return BigIntBuffer.toBigIntLE(res_buf); 44 | } 45 | 46 | /* 47 | Verifies ECDSA signature of a given message hash z with a given public key. 48 | Returns true if public_key signs the message. 49 | NOTE: This function assumes that the public_key is on the curve. 50 | */ 51 | function verify(stark_key, message_hash, r, s) { 52 | const stark_key_buf = BigIntBuffer.toBufferLE(stark_key, 32); 53 | const message_hash_buf = BigIntBuffer.toBufferLE(message_hash, 32); 54 | const r_buf = BigIntBuffer.toBufferLE(r, 32); 55 | const bnS = new BN(s.toString(16), 16); 56 | const w = BigInt('0x' + bnS.invm(curveOrder).toString(16), 16) 57 | const s_buf = BigIntBuffer.toBufferLE(w, 32); 58 | return libcrypto.Verify(stark_key_buf, message_hash_buf, r_buf, s_buf); 59 | } 60 | 61 | /* 62 | Signs the given message hash with the provided private_key, with randomness k. 63 | 64 | NOTE: k should be a strong cryptographical random, and not repeat. 65 | See: https://tools.ietf.org/html/rfc6979. 66 | */ 67 | function sign(private_key, message, k) { 68 | const private_key_buf = BigIntBuffer.toBufferLE(private_key, 32); 69 | const message_buf = BigIntBuffer.toBufferLE(message, 32); 70 | const k_buf = BigIntBuffer.toBufferLE(k, 32); 71 | const res_buf = Buffer.alloc(1024); 72 | const res = libcrypto.Sign(private_key_buf, message_buf, k_buf, res_buf); 73 | assert(res == 0, 'Error: ' + res_buf.toString('utf-8')); 74 | const r = BigIntBuffer.toBigIntLE(res_buf.slice(0, 32)); 75 | const w = BigIntBuffer.toBigIntLE(res_buf.slice(32, 64)); 76 | const bnW = new BN(w.toString(16), 16) 77 | const s = BigInt('0x' + bnW.invm(curveOrder).toString(16), 16) 78 | return {r: r, s: s}; 79 | } 80 | 81 | /* 82 | Deduces the public key given a private key. 83 | The x coordinate of the public key is also known as the partial public key, 84 | and used in StarkEx to identify the user. 85 | */ 86 | function getPublicKey(private_key) { 87 | const private_key_buf = BigIntBuffer.toBufferLE(private_key, 32); 88 | const res_buf = Buffer.alloc(1024); 89 | const res = libcrypto.GetPublicKey(private_key_buf, res_buf); 90 | assert(res == 0, 'Error: ' + res_buf.toString('utf-8')); 91 | return BigIntBuffer.toBigIntLE(res_buf); 92 | } 93 | 94 | module.exports = { 95 | pedersen, 96 | sign, 97 | verify, 98 | getPublicKey 99 | }; 100 | -------------------------------------------------------------------------------- /src/starkware/crypto/ffi/js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starkware_crypto_ffi", 3 | "version": "1.0.0", 4 | "description": "Fast stark-friendly crypto", 5 | "main": "crypto.js", 6 | "scripts": { 7 | "test": "mocha" 8 | }, 9 | "keywords": [ 10 | "stark", 11 | "crypto", 12 | "ffi" 13 | ], 14 | "author": "StarkWare Industries Ltd.", 15 | "license": "Apache-2.0", 16 | "dependencies": { 17 | "bigint-buffer": "^1.1.5", 18 | "bn.js": "^5.2.0", 19 | "ffi-napi": "^3.1.0" 20 | }, 21 | "devDependencies": { 22 | "chai": "^4.2.0", 23 | "mocha": "^8.2.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/starkware/crypto/ffi/js/test/crypto_test.js: -------------------------------------------------------------------------------- 1 | const swCrypto = require('../crypto.js'); 2 | const chai = require('chai'); 3 | const { expect } = chai; 4 | 5 | describe('Pedersen Hash', () => { 6 | it('should hash correctly', () => { 7 | const testData = require('./signature_test_data.json'); 8 | for (const hashTestData of [ 9 | testData.hash_test.pedersen_hash_data_1, testData.hash_test.pedersen_hash_data_2 10 | ]) { 11 | expect( 12 | swCrypto.pedersen( 13 | BigInt(hashTestData.input_1), 14 | BigInt(hashTestData.input_2) 15 | ) 16 | ).to.equal( 17 | BigInt(hashTestData.output) 18 | ); 19 | } 20 | }); 21 | }); 22 | 23 | describe('getPublicKey', () => { 24 | it('should derive correct public key', () => { 25 | expect( 26 | swCrypto.getPublicKey(BigInt('0x1')) 27 | ).to.equal( 28 | BigInt('0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca') 29 | ); 30 | }); 31 | }); 32 | 33 | describe('Verify', () => { 34 | const stark_key = BigInt('0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca'); 35 | const message = BigInt('0x2'); 36 | const r = BigInt('0x411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20'); 37 | const s = BigInt('0x405c3191ab3883ef2b763af35bc5f5d15b3b4e99461d70e84c654a351a7c81b'); 38 | it('should verify', () => { 39 | expect(swCrypto.verify(stark_key, message, r, s)).to.equal(true); 40 | }); 41 | }); 42 | 43 | describe('Sign', () => { 44 | const private_key = BigInt('0x1'); 45 | const message = BigInt('0x2'); 46 | const k = BigInt('0x3'); 47 | it('correct signature', () => { 48 | expected_r = BigInt('0x411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20') 49 | expected_s = BigInt('0x405c3191ab3883ef2b763af35bc5f5d15b3b4e99461d70e84c654a351a7c81b') 50 | const { r, s } = swCrypto.sign(private_key, message, k) 51 | expect(r).to.equal(expected_r); 52 | expect(s).to.equal(expected_s); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /src/starkware/crypto/ffi/js/test/signature_test_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "hash_test": { 3 | "pedersen_hash_data_1": { 4 | "input_1": "0x3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb", 5 | "input_2": "0x208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a", 6 | "output": "0x30e480bed5fe53fa909cc0f8c4d99b8f9f2c016be4c41e13a4848797979c662" 7 | }, 8 | "pedersen_hash_data_2": { 9 | "input_1": "0x58f580910a6ca59b28927c08fe6c43e2e303ca384badc365795fc645d479d45", 10 | "input_2": "0x78734f65a067be9bdb39de18434d71e79f7b6466a4b66bbd979ab9e7515fe0b", 11 | "output": "0x68cc0b76cddd1dd4ed2301ada9b7c872b23875d5ff837b3a87993e0d9996b87" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/starkware/crypto/ffi/pedersen_hash.cc: -------------------------------------------------------------------------------- 1 | #include "starkware/crypto/ffi/pedersen_hash.h" 2 | #include "starkware/crypto/pedersen_hash.h" 3 | 4 | #include 5 | 6 | #include "starkware/algebra/prime_field_element.h" 7 | #include "starkware/crypto/ffi/utils.h" 8 | 9 | #include "third_party/gsl/gsl-lite.hpp" 10 | 11 | namespace starkware { 12 | 13 | namespace { 14 | 15 | using ValueType = PrimeFieldElement::ValueType; 16 | 17 | constexpr size_t kElementSize = sizeof(ValueType); 18 | constexpr size_t kOutBufferSize = 1024; 19 | static_assert(kOutBufferSize >= kElementSize, "kOutBufferSize is not big enough"); 20 | 21 | } // namespace 22 | 23 | extern "C" int Hash( 24 | const gsl::byte in1[kElementSize], const gsl::byte in2[kElementSize], 25 | gsl::byte out[kOutBufferSize]) { 26 | try { 27 | auto hash = PedersenHash( 28 | PrimeFieldElement::FromBigInt(Deserialize(gsl::make_span(in1, kElementSize))), 29 | PrimeFieldElement::FromBigInt(Deserialize(gsl::make_span(in2, kElementSize)))); 30 | Serialize(hash.ToStandardForm(), gsl::make_span(out, kElementSize)); 31 | } catch (const std::exception& e) { 32 | return HandleError(e.what(), gsl::make_span(out, kOutBufferSize)); 33 | } catch (...) { 34 | return HandleError("Unknown c++ exception.", gsl::make_span(out, kOutBufferSize)); 35 | } 36 | return 0; 37 | } 38 | 39 | } // namespace starkware 40 | -------------------------------------------------------------------------------- /src/starkware/crypto/ffi/pedersen_hash.h: -------------------------------------------------------------------------------- 1 | #ifndef STARKWARE_CRYPTO_FFI_PEDERSEN_HASH_H_ 2 | #define STARKWARE_CRYPTO_FFI_PEDERSEN_HASH_H_ 3 | 4 | int Hash(const char* in1, const char* in2, char* out); 5 | 6 | #endif // STARKWARE_CRYPTO_FFI_PEDERSEN_HASH_H_ 7 | -------------------------------------------------------------------------------- /src/starkware/crypto/ffi/portable_endian.h: -------------------------------------------------------------------------------- 1 | #ifndef STARKWARE_CRYPTO_FFI_PORTABLE_ENDIAN_H_ 2 | #define STARKWARE_CRYPTO_FFI_PORTABLE_ENDIAN_H_ 3 | 4 | #if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__) 5 | 6 | # define __WINDOWS__ 7 | 8 | #endif 9 | 10 | #if defined(__linux__) || defined(__CYGWIN__) 11 | 12 | # include 13 | 14 | #elif defined(__APPLE__) 15 | 16 | # include 17 | 18 | # define htobe16(x) OSSwapHostToBigInt16(x) 19 | # define htole16(x) OSSwapHostToLittleInt16(x) 20 | # define be16toh(x) OSSwapBigToHostInt16(x) 21 | # define le16toh(x) OSSwapLittleToHostInt16(x) 22 | 23 | # define htobe32(x) OSSwapHostToBigInt32(x) 24 | # define htole32(x) OSSwapHostToLittleInt32(x) 25 | # define be32toh(x) OSSwapBigToHostInt32(x) 26 | # define le32toh(x) OSSwapLittleToHostInt32(x) 27 | 28 | # define htobe64(x) OSSwapHostToBigInt64(x) 29 | # define htole64(x) OSSwapHostToLittleInt64(x) 30 | # define be64toh(x) OSSwapBigToHostInt64(x) 31 | # define le64toh(x) OSSwapLittleToHostInt64(x) 32 | 33 | # define __BYTE_ORDER BYTE_ORDER 34 | # define __BIG_ENDIAN BIG_ENDIAN 35 | # define __LITTLE_ENDIAN LITTLE_ENDIAN 36 | # define __PDP_ENDIAN PDP_ENDIAN 37 | 38 | #elif defined(__OpenBSD__) 39 | 40 | # include 41 | 42 | #elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) 43 | 44 | # include 45 | 46 | # define be16toh(x) betoh16(x) 47 | # define le16toh(x) letoh16(x) 48 | 49 | # define be32toh(x) betoh32(x) 50 | # define le32toh(x) letoh32(x) 51 | 52 | # define be64toh(x) betoh64(x) 53 | # define le64toh(x) letoh64(x) 54 | 55 | #elif defined(__WINDOWS__) 56 | 57 | # include 58 | # include 59 | 60 | # if BYTE_ORDER == LITTLE_ENDIAN 61 | 62 | # define htobe16(x) htons(x) 63 | # define htole16(x) (x) 64 | # define be16toh(x) ntohs(x) 65 | # define le16toh(x) (x) 66 | 67 | # define htobe32(x) htonl(x) 68 | # define htole32(x) (x) 69 | # define be32toh(x) ntohl(x) 70 | # define le32toh(x) (x) 71 | 72 | # define htobe64(x) htonll(x) 73 | # define htole64(x) (x) 74 | # define be64toh(x) ntohll(x) 75 | # define le64toh(x) (x) 76 | 77 | # elif BYTE_ORDER == BIG_ENDIAN 78 | 79 | /* that would be xbox 360 */ 80 | # define htobe16(x) (x) 81 | # define htole16(x) __builtin_bswap16(x) 82 | # define be16toh(x) (x) 83 | # define le16toh(x) __builtin_bswap16(x) 84 | 85 | # define htobe32(x) (x) 86 | # define htole32(x) __builtin_bswap32(x) 87 | # define be32toh(x) (x) 88 | # define le32toh(x) __builtin_bswap32(x) 89 | 90 | # define htobe64(x) (x) 91 | # define htole64(x) __builtin_bswap64(x) 92 | # define be64toh(x) (x) 93 | # define le64toh(x) __builtin_bswap64(x) 94 | 95 | # else 96 | 97 | # error byte order not supported 98 | 99 | # endif 100 | 101 | # define __BYTE_ORDER BYTE_ORDER 102 | # define __BIG_ENDIAN BIG_ENDIAN 103 | # define __LITTLE_ENDIAN LITTLE_ENDIAN 104 | # define __PDP_ENDIAN PDP_ENDIAN 105 | 106 | #else 107 | 108 | # error platform not supported 109 | 110 | #endif 111 | 112 | #endif // STARKWARE_CRYPTO_FFI_PORTABLE_ENDIAN_H_ -------------------------------------------------------------------------------- /src/starkware/crypto/ffi/utils.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "starkware/crypto/ffi/utils.h" 5 | #include "starkware/crypto/ffi/portable_endian.h" 6 | 7 | namespace starkware { 8 | 9 | using ValueType = PrimeFieldElement::ValueType; 10 | 11 | int HandleError(const char* msg, gsl::span out) { 12 | const size_t copy_len = std::min(strlen(msg), out.size() - 1); 13 | memcpy(out.data(), msg, copy_len); 14 | memset(out.data() + copy_len, 0, out.size() - copy_len); 15 | return 1; 16 | } 17 | 18 | ValueType Deserialize(const gsl::span span) { 19 | const size_t N = ValueType::LimbCount(); 20 | ASSERT(span.size() == N * sizeof(uint64_t), "Source span size mismatches BigInt size."); 21 | std::array value{}; 22 | gsl::copy(span, gsl::byte_span(value)); 23 | for (uint64_t& x : value) { 24 | x = le64toh(x); 25 | } 26 | return ValueType(value); 27 | } 28 | 29 | void Serialize(const ValueType& val, const gsl::span span_out) { 30 | const size_t N = ValueType::LimbCount(); 31 | ASSERT(span_out.size() == N * sizeof(uint64_t), "Span size mismatches BigInt size."); 32 | for (size_t i = 0; i < N; ++i) { 33 | uint64_t limb = htole64(val[i]); 34 | gsl::copy(gsl::byte_span(limb), span_out.subspan(i * sizeof(uint64_t), sizeof(uint64_t))); 35 | } 36 | } 37 | 38 | } // namespace starkware 39 | -------------------------------------------------------------------------------- /src/starkware/crypto/ffi/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef STARKWARE_CRYPTO_FFI_UTILS_H_ 2 | #define STARKWARE_CRYPTO_FFI_UTILS_H_ 3 | 4 | #include 5 | 6 | #include "starkware/crypto/pedersen_hash.h" 7 | 8 | #include "third_party/gsl/gsl-lite.hpp" 9 | 10 | namespace starkware { 11 | 12 | using ValueType = PrimeFieldElement::ValueType; 13 | 14 | /* 15 | Handles an error, and outputs a relevant error message as a C string to out. 16 | */ 17 | int HandleError(const char* msg, gsl::span out); 18 | 19 | /* 20 | Deserializes a BigInt (PrimeFieldElement::ValueType) from a byte span. 21 | */ 22 | ValueType Deserialize(const gsl::span span); 23 | 24 | /* 25 | Serializes a BigInt (PrimeFieldElement::ValueType) to a byte span. 26 | */ 27 | void Serialize(const ValueType& val, const gsl::span span_out); 28 | 29 | } // namespace starkware 30 | 31 | #endif // STARKWARE_CRYPTO_FFI_UTILS_H_ 32 | -------------------------------------------------------------------------------- /src/starkware/crypto/pedersen_hash.cc: -------------------------------------------------------------------------------- 1 | #include "starkware/crypto/pedersen_hash.h" 2 | 3 | #include 4 | 5 | #include "starkware/algebra/elliptic_curve.h" 6 | #include "starkware/algebra/fraction_field_element.h" 7 | #include "starkware/crypto/elliptic_curve_constants.h" 8 | #include "starkware/utils/error_handling.h" 9 | 10 | namespace starkware { 11 | 12 | namespace { 13 | 14 | EcPoint> EcSubsetSumHash( 15 | const EcPoint>& shift_point, 16 | gsl::span> points, const PrimeFieldElement& selector_value) { 17 | using FractionFieldElementT = FractionFieldElement; 18 | const auto selector_value_as_big_int = selector_value.ToStandardForm(); 19 | const std::vector selector_bits = selector_value_as_big_int.ToBoolVector(); 20 | ASSERT(points.size() <= selector_bits.size(), "Too many points."); 21 | 22 | auto partial_sum = shift_point; 23 | for (size_t j = 0; j < points.size(); j++) { 24 | const auto point = points[j].template ConvertTo(); 25 | ASSERT(partial_sum.x != point.x, "Adding a point to itself or to its inverse point."); 26 | if (selector_bits[j]) { 27 | partial_sum = partial_sum + point; 28 | } 29 | } 30 | for (size_t j = points.size(); j < selector_bits.size(); j++) { 31 | ASSERT(selector_bits[j] == 0, "Given selector is too big."); 32 | } 33 | return partial_sum; 34 | } 35 | 36 | } // namespace 37 | 38 | PrimeFieldElement PedersenHash(const PrimeFieldElement& x, const PrimeFieldElement& y) { 39 | const size_t n_element_bits = 252; 40 | const auto& consts = GetEcConstants(); 41 | const auto& shift_point = consts.k_points[0]; 42 | const auto points_span = gsl::make_span(consts.k_points).subspan(2); 43 | 44 | auto cur_sum = shift_point.template ConvertTo>(); 45 | cur_sum = EcSubsetSumHash(cur_sum, points_span.subspan(0, n_element_bits), x); 46 | cur_sum = EcSubsetSumHash(cur_sum, points_span.subspan(n_element_bits, n_element_bits), y); 47 | 48 | const EcPoint res = cur_sum.template ConvertTo(); 49 | return res.x; 50 | } 51 | 52 | } // namespace starkware 53 | -------------------------------------------------------------------------------- /src/starkware/crypto/pedersen_hash.h: -------------------------------------------------------------------------------- 1 | #ifndef STARKWARE_CRYPTO_PEDERSEN_HASH_H_ 2 | #define STARKWARE_CRYPTO_PEDERSEN_HASH_H_ 3 | 4 | #include "third_party/gsl/gsl-lite.hpp" 5 | 6 | #include "starkware/algebra/prime_field_element.h" 7 | 8 | namespace starkware { 9 | 10 | /* 11 | Computes the Starkware version of the Pedersen hash of x and y. 12 | The hash is defined by: 13 | shift_point + x_low * P_0 + x_high * P1 + y_low * P2 + y_high * P3 14 | where x_low is the 248 low bits of x, x_high is the 4 high bits of x and similarly for y. 15 | shift_point, P_0, P_1, P_2, P_3 are constant points generated from the digits of pi. 16 | */ 17 | PrimeFieldElement PedersenHash(const PrimeFieldElement& x, const PrimeFieldElement& y); 18 | 19 | } // namespace starkware 20 | 21 | #endif // STARKWARE_CRYPTO_PEDERSEN_HASH_H_ 22 | -------------------------------------------------------------------------------- /src/starkware/crypto/pedersen_hash_test.cc: -------------------------------------------------------------------------------- 1 | #include "starkware/crypto/pedersen_hash.h" 2 | 3 | #include "gtest/gtest.h" 4 | 5 | namespace starkware { 6 | namespace { 7 | 8 | TEST(PedersenHash, Zero) { 9 | EXPECT_EQ( 10 | PedersenHash(PrimeFieldElement::Zero(), PrimeFieldElement::Zero()), 11 | PrimeFieldElement::FromBigInt( 12 | 0x49ee3eba8c1600700ee1b87eb599f16716b0b1022947733551fde4050ca6804_Z)); 13 | } 14 | 15 | TEST(PedersenHash, Correctness) { 16 | const auto x = PrimeFieldElement::FromBigInt( 17 | 0x3d937c035c878245caf64531a5756109c53068da139362728feb561405371cb_Z); 18 | const auto y = PrimeFieldElement::FromBigInt( 19 | 0x208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a_Z); 20 | const auto expected_result = PrimeFieldElement::FromBigInt( 21 | 0x30e480bed5fe53fa909cc0f8c4d99b8f9f2c016be4c41e13a4848797979c662_Z); 22 | EXPECT_EQ(PedersenHash(x, y), expected_result); 23 | } 24 | 25 | TEST(PedersenHash, Benchmark) { 26 | Prng prng; 27 | auto res = PrimeFieldElement::Zero(); 28 | for (size_t i = 0; i < 1000; i++) { 29 | const auto y = PrimeFieldElement::RandomElement(&prng); 30 | res = PedersenHash(res, y); 31 | } 32 | EXPECT_NE(res, PrimeFieldElement::Zero()); 33 | } 34 | 35 | } // namespace 36 | } // namespace starkware 37 | -------------------------------------------------------------------------------- /src/starkware/starkex/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(starkex order.cc) 2 | target_link_libraries(starkex crypto) 3 | 4 | add_executable(order_test order_test.cc) 5 | target_link_libraries(order_test starkex gtest gtest_main pthread) 6 | add_test(order_test order_test) 7 | -------------------------------------------------------------------------------- /src/starkware/starkex/order.cc: -------------------------------------------------------------------------------- 1 | #include "starkware/starkex/order.h" 2 | 3 | #include "starkware/crypto/pedersen_hash.h" 4 | 5 | namespace starkware { 6 | 7 | namespace { 8 | 9 | PrimeFieldElement GetOrderPackedMessage( 10 | uint64_t order_type, uint64_t vault_a, uint64_t vault_b, uint64_t amount_a, uint64_t amount_b, 11 | uint64_t nonce, uint64_t expiration_timestamp) { 12 | static constexpr uint64_t kOrderTypeLimit = 3; 13 | static constexpr uint64_t kVaultIdLimit = Pow2(31); 14 | static constexpr uint64_t kAmountLimit = Pow2(63); 15 | static constexpr uint64_t kNonceLimit = Pow2(31); 16 | static constexpr uint64_t kExpirationTimestampLimit = Pow2(22); 17 | 18 | ASSERT(order_type < kOrderTypeLimit, "Invalid order_type."); 19 | ASSERT(vault_a < kVaultIdLimit, "vault_a is out of range."); 20 | ASSERT(vault_b < kVaultIdLimit, "vault_b is out of range."); 21 | ASSERT(amount_a < kAmountLimit, "amount_a is out of range."); 22 | ASSERT(amount_b < kAmountLimit, "amount_b is out of range."); 23 | ASSERT(nonce < kNonceLimit, "nonce is out of range."); 24 | ASSERT(expiration_timestamp < kExpirationTimestampLimit, "expiration_timestamp is out of range."); 25 | 26 | PrimeFieldElement packed_message = PrimeFieldElement::FromUint(order_type); 27 | packed_message = (packed_message * PrimeFieldElement::FromUint(kVaultIdLimit)) + 28 | PrimeFieldElement::FromUint(vault_a); 29 | packed_message = (packed_message * PrimeFieldElement::FromUint(kVaultIdLimit)) + 30 | PrimeFieldElement::FromUint(vault_b); 31 | packed_message = (packed_message * PrimeFieldElement::FromUint(kAmountLimit)) + 32 | PrimeFieldElement::FromUint(amount_a); 33 | packed_message = (packed_message * PrimeFieldElement::FromUint(kAmountLimit)) + 34 | PrimeFieldElement::FromUint(amount_b); 35 | packed_message = (packed_message * PrimeFieldElement::FromUint(kNonceLimit)) + 36 | PrimeFieldElement::FromUint(nonce); 37 | packed_message = (packed_message * PrimeFieldElement::FromUint(kExpirationTimestampLimit)) + 38 | PrimeFieldElement::FromUint(expiration_timestamp); 39 | return packed_message; 40 | } 41 | 42 | } // namespace 43 | 44 | PrimeFieldElement GetSettlementOrderMessage( 45 | uint64_t vault_id_sell, uint64_t vault_id_buy, uint64_t amount_sell, uint64_t amount_buy, 46 | uint64_t nonce, uint64_t expiration_timestamp, const PrimeFieldElement& token_sell, 47 | const PrimeFieldElement& token_buy) { 48 | return PedersenHash( 49 | PedersenHash(token_sell, token_buy), 50 | GetOrderPackedMessage( 51 | 0, vault_id_sell, vault_id_buy, amount_sell, amount_buy, nonce, expiration_timestamp)); 52 | } 53 | 54 | PrimeFieldElement GetTransferOrderMessage( 55 | uint64_t sender_vault_id, uint64_t target_vault_id, uint64_t amount, uint64_t nonce, 56 | uint64_t expiration_timestamp, const PrimeFieldElement& token, 57 | const PrimeFieldElement& target_public_key) { 58 | return PedersenHash( 59 | PedersenHash(token, target_public_key), 60 | GetOrderPackedMessage( 61 | 1, sender_vault_id, target_vault_id, amount, 0, nonce, expiration_timestamp)); 62 | } 63 | 64 | PrimeFieldElement GetConditionalTransferOrderMessage( 65 | uint64_t sender_vault_id, uint64_t target_vault_id, uint64_t amount, uint64_t nonce, 66 | uint64_t expiration_timestamp, const PrimeFieldElement& token, 67 | const PrimeFieldElement& target_public_key, const PrimeFieldElement& condition) { 68 | return PedersenHash( 69 | PedersenHash(PedersenHash(token, target_public_key), condition), 70 | GetOrderPackedMessage( 71 | 2, sender_vault_id, target_vault_id, amount, 0, nonce, expiration_timestamp)); 72 | } 73 | 74 | uint64_t GetOrderIdFromMessage(const PrimeFieldElement& message) { 75 | const auto bigint = message.ToStandardForm(); 76 | static_assert(bigint.LimbCount() == 4); 77 | // bigint must be smaller than 2^251, so the high limb must be below 2^59 (59 = 251 - 64 * 3). 78 | ASSERT(bigint[3] < Pow2(59), "message is out of range."); 79 | // Order id is the 63 high bits of a 251-bit number, so we take 60 bits from the high limb and 4 80 | // more bits from the next limb. 81 | return (bigint[3] << 4) | (bigint[2] >> 60); 82 | } 83 | 84 | } // namespace starkware 85 | -------------------------------------------------------------------------------- /src/starkware/starkex/order.h: -------------------------------------------------------------------------------- 1 | #ifndef STARKWARE_STARKEX_ORDER_H_ 2 | #define STARKWARE_STARKEX_ORDER_H_ 3 | 4 | #include "starkware/algebra/prime_field_element.h" 5 | 6 | namespace starkware { 7 | 8 | /* 9 | Serializes the order message in the canonical format expected by the verifier. 10 | party_a sells amount_sell coins of token_sell from vault_id_sell. 11 | party_a buys amount_buy coins of token_buy into vault_id_buy. 12 | expiration_timestamp - the number of hours that have elapsed since 01/01/1970 (i.e., 13 | Unix timstamp / 3600). 14 | */ 15 | PrimeFieldElement GetSettlementOrderMessage( 16 | uint64_t vault_id_sell, uint64_t vault_id_buy, uint64_t amount_sell, uint64_t amount_buy, 17 | uint64_t nonce, uint64_t expiration_timestamp, const PrimeFieldElement& token_sell, 18 | const PrimeFieldElement& token_buy); 19 | 20 | /* 21 | Serializes the transfer message in the canonical format expected by the verifier. 22 | The sender transfers amount coins of type token to target_public_key, from sender_vault_id 23 | to target_vault_id. 24 | expiration_timestamp - the number of hours that have elapsed since 01/01/1970 (i.e., 25 | Unix timstamp / 3600). 26 | */ 27 | PrimeFieldElement GetTransferOrderMessage( 28 | uint64_t sender_vault_id, uint64_t target_vault_id, uint64_t amount, uint64_t nonce, 29 | uint64_t expiration_timestamp, const PrimeFieldElement& token, 30 | const PrimeFieldElement& target_public_key); 31 | 32 | /* 33 | Serializes the conditional transfer message in the canonical format expected by the verifier. 34 | The sender transfers amount coins of type token to target_public_key, from sender_vault_id 35 | to target_vault_id, only if condition is valid. 36 | expiration_timestamp - the number of hours that have elapsed since 01/01/1970 (i.e., 37 | Unix timstamp / 3600). 38 | */ 39 | PrimeFieldElement GetConditionalTransferOrderMessage( 40 | uint64_t sender_vault_id, uint64_t target_vault_id, uint64_t amount, uint64_t nonce, 41 | uint64_t expiration_timestamp, const PrimeFieldElement& token, 42 | const PrimeFieldElement& target_public_key, const PrimeFieldElement& condition); 43 | 44 | /* 45 | Returns the 63-bit order id based on the order message (the result of GetSettlementOrderMessage() 46 | or GetTransferOrderMessage()). 47 | */ 48 | uint64_t GetOrderIdFromMessage(const PrimeFieldElement& message); 49 | 50 | } // namespace starkware 51 | 52 | #endif // STARKWARE_STARKEX_ORDER_H_ 53 | -------------------------------------------------------------------------------- /src/starkware/starkex/order_test.cc: -------------------------------------------------------------------------------- 1 | #include "starkware/starkex/order.h" 2 | 3 | #include "gmock/gmock.h" 4 | #include "gtest/gtest.h" 5 | 6 | #include "starkware/utils/test_utils.h" 7 | 8 | namespace starkware { 9 | namespace { 10 | 11 | using testing::HasSubstr; 12 | 13 | TEST(SettlementOrder, Regression) { 14 | // The following hard-coded values were cross-checked with a number of implementations. 15 | const auto message = GetSettlementOrderMessage( 16 | 21, 27, 2154686749748910716, 1470242115489520459, 0, 438953, 17 | PrimeFieldElement::FromBigInt( 18 | 0x5fa3383597691ea9d827a79e1a4f0f7989c35ced18ca9619de8ab97e661020_Z), 19 | PrimeFieldElement::FromBigInt( 20 | 0x774961c824a3b0fb3d2965f01471c9c7734bf8dbde659e0c08dca2ef18d56a_Z)); 21 | const auto expected_message = PrimeFieldElement::FromBigInt( 22 | 0x397e76d1667c4454bfb83514e120583af836f8e32a516765497823eabe16a3f_Z); 23 | EXPECT_EQ(message, expected_message); 24 | EXPECT_EQ(GetOrderIdFromMessage(message), 4142879348967097428); 25 | } 26 | 27 | TEST(TransferOrder, Regression) { 28 | // The following hard-coded values were cross-checked with a number of implementations. 29 | const auto message = GetTransferOrderMessage( 30 | 34, 21, 2154549703648910716, 1, 438953, 31 | PrimeFieldElement::FromBigInt( 32 | 0x3003a65651d3b9fb2eff934a4416db301afd112a8492aaf8d7297fc87dcd9f4_Z), 33 | PrimeFieldElement::FromBigInt( 34 | 0x5fa3383597691ea9d827a79e1a4f0f7949435ced18ca9619de8ab97e661020_Z)); 35 | const auto expected_message = PrimeFieldElement::FromBigInt( 36 | 0x6366b00c218fb4c8a8b142ca482145e8513c78e00faa0de76298ba14fc37ae7_Z); 37 | EXPECT_EQ(message, expected_message); 38 | EXPECT_EQ(GetOrderIdFromMessage(message), 7162605823528514760); 39 | } 40 | 41 | TEST(ConditionalTransferOrder, Regression) { 42 | // The following hard-coded values were cross-checked with a number of implementations. 43 | const auto message = GetConditionalTransferOrderMessage( 44 | 34, 21, 2154549703648910716, 1, 438953, 45 | PrimeFieldElement::FromBigInt( 46 | 0x3003a65651d3b9fb2eff934a4416db301afd112a8492aaf8d7297fc87dcd9f4_Z), 47 | PrimeFieldElement::FromBigInt( 48 | 0x5fa3383597691ea9d827a79e1a4f0f7949435ced18ca9619de8ab97e661020_Z), 49 | PrimeFieldElement::FromBigInt( 50 | 0x318ff6d26cf3175c77668cd6434ab34d31e59f806a6a7c06d08215bccb7eaf8_Z)); 51 | const auto expected_message = PrimeFieldElement::FromBigInt( 52 | 0xfa5f0ad1ebff93c9e6474379a213ba1e1f9e42f5f1cb361b0327e073720384_Z); 53 | EXPECT_EQ(message, expected_message); 54 | EXPECT_EQ(GetOrderIdFromMessage(message), 1127571908062083388); 55 | } 56 | 57 | TEST(GetOrderIdFromMessage, Correctness) { 58 | Prng prng; 59 | const uint64_t order_id = prng.RandomUint64(0, Pow2(63) - 1); 60 | const auto message = 61 | PrimeFieldElement::FromUint(order_id) * PrimeFieldElement::FromUint(2).Pow(251 - 63); 62 | EXPECT_EQ(GetOrderIdFromMessage(message), order_id); 63 | } 64 | 65 | TEST(GetOrderIdFromMessage, MessageTooLarge) { 66 | Prng prng; 67 | const auto message = PrimeFieldElement::FromUint(2).Pow(251); 68 | EXPECT_ASSERT(GetOrderIdFromMessage(message), HasSubstr("message is out of range")); 69 | } 70 | 71 | } // namespace 72 | } // namespace starkware 73 | -------------------------------------------------------------------------------- /src/starkware/utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(math_test math_test.cc) 2 | target_link_libraries(math_test gtest gtest_main pthread) 3 | add_test(math_test math_test) 4 | 5 | add_executable(prng_test prng_test.cc) 6 | target_link_libraries(prng_test gtest gtest_main pthread) 7 | add_test(prng_test prng_test) 8 | -------------------------------------------------------------------------------- /src/starkware/utils/error_handling.h: -------------------------------------------------------------------------------- 1 | #ifndef STARKWARE_UTILS_ERROR_HANDLING_H_ 2 | #define STARKWARE_UTILS_ERROR_HANDLING_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace starkware { 9 | 10 | class StarkwareException : public std::exception { 11 | public: 12 | explicit StarkwareException(std::string message) : message_(std::move(message)) {} 13 | const char* what() const noexcept { return message_.c_str(); } // NOLINT 14 | 15 | private: 16 | std::string message_; 17 | }; 18 | 19 | /* 20 | We use "do {} while(false);" pattern to force the user to use ; after the macro. 21 | */ 22 | #define ASSERT(cond, msg) \ 23 | do { \ 24 | if (!(cond)) { \ 25 | throw StarkwareException(msg); \ 26 | } \ 27 | } while (false) 28 | 29 | } // namespace starkware 30 | 31 | #endif // STARKWARE_UTILS_ERROR_HANDLING_H_ 32 | -------------------------------------------------------------------------------- /src/starkware/utils/math.h: -------------------------------------------------------------------------------- 1 | #ifndef STARKWARE_UTILS_MATH_H_ 2 | #define STARKWARE_UTILS_MATH_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "starkware/utils/error_handling.h" 8 | 9 | namespace starkware { 10 | 11 | using std::size_t; 12 | 13 | constexpr uint64_t inline Pow2(uint64_t n) { 14 | ASSERT(n < 64, "n must be smaller than 64."); 15 | return UINT64_C(1) << n; 16 | } 17 | 18 | /* 19 | Returns floor(Log_2(n)), n must be > 0. 20 | */ 21 | constexpr size_t inline Log2Floor(const uint64_t n) { 22 | ASSERT(n != 0, "log2 of 0 is undefined"); 23 | static_assert(sizeof(long long) == 8, "It is assumed that long long is 64bits"); // NOLINT 24 | return 63 - __builtin_clzll(n); 25 | } 26 | 27 | /* 28 | Computes base to the power of the number given by exponent_bits in a generic group, given the 29 | element one in the group and a function mult(const GroupElementT& multiplier, GroupElementT* dst) 30 | that performs: 31 | *dst *= multiplier 32 | in the group. 33 | Note that it is possible that the address of multiplier is the same as dst. 34 | */ 35 | template 36 | GroupElementT GenericPow( 37 | const GroupElementT& base, const std::vector& exponent_bits, const GroupElementT& one, 38 | const MultFunc& mult) { 39 | GroupElementT power = base; 40 | GroupElementT res = one; 41 | for (const auto&& b : exponent_bits) { 42 | if (b) { 43 | mult(power, &res); 44 | } 45 | 46 | mult(power, &power); 47 | } 48 | 49 | return res; 50 | } 51 | 52 | } // namespace starkware 53 | 54 | #endif // STARKWARE_UTILS_MATH_H_ 55 | -------------------------------------------------------------------------------- /src/starkware/utils/math_test.cc: -------------------------------------------------------------------------------- 1 | #include "starkware/utils/math.h" 2 | 3 | #include "gmock/gmock.h" 4 | #include "gtest/gtest.h" 5 | 6 | #include "starkware/utils/prng.h" 7 | #include "starkware/utils/test_utils.h" 8 | 9 | namespace starkware { 10 | namespace { 11 | 12 | using testing::HasSubstr; 13 | 14 | TEST(Math, Pow2) { EXPECT_EQ(32U, Pow2(5)); } 15 | TEST(Math, Pow2_0) { EXPECT_EQ(1U, Pow2(0)); } 16 | TEST(Math, Pow2_63) { EXPECT_EQ(UINT64_C(0x8000000000000000), Pow2(63)); } 17 | 18 | TEST(Math, Log2Floor_0) { EXPECT_ASSERT(Log2Floor(0), HasSubstr("log2 of 0 is undefined")); } 19 | TEST(Math, Log2Floor_1) { EXPECT_EQ(Log2Floor(1), 0U); } 20 | TEST(Math, Log2Floor_31) { EXPECT_EQ(Log2Floor(31), 4U); } 21 | TEST(Math, Log2Floor_32) { EXPECT_EQ(Log2Floor(32), 5U); } 22 | TEST(Math, Log2Floor_33) { EXPECT_EQ(Log2Floor(33), 5U); } 23 | TEST(Math, Log2Floor_AllBitsSet) { EXPECT_EQ(Log2Floor(UINT64_C(0xffffffffffffffff)), 63U); } 24 | 25 | TEST(Math, GenericPow) { 26 | Prng prng; 27 | const uint64_t random_base = prng.RandomUint64(0, 31); 28 | const size_t exponent = 11; 29 | std::vector exp_bits{true, true, false, true}; 30 | // Create pow result. 31 | uint64_t pow_res = 1; 32 | for (size_t i = 0; i < exponent; ++i) { 33 | pow_res *= random_base; 34 | } 35 | 36 | EXPECT_EQ( 37 | pow_res, 38 | GenericPow(random_base, exp_bits, uint64_t(1), [](const uint64_t& multiplier, uint64_t* dst) { 39 | *dst *= multiplier; 40 | })); 41 | } 42 | 43 | } // namespace 44 | } // namespace starkware 45 | -------------------------------------------------------------------------------- /src/starkware/utils/prng.h: -------------------------------------------------------------------------------- 1 | #ifndef STARKWARE_UTILS_PRNG_H_ 2 | #define STARKWARE_UTILS_PRNG_H_ 3 | 4 | #include 5 | #include 6 | 7 | namespace starkware { 8 | 9 | class Prng { 10 | public: 11 | Prng() : mt_prng_(std::random_device()()) {} 12 | 13 | /* 14 | Returns a random integer in the range [lower_bound, upper_bound]. 15 | */ 16 | uint64_t RandomUint64(uint64_t lower_bound, uint64_t upper_bound) { 17 | return std::uniform_int_distribution(lower_bound, upper_bound)(mt_prng_); 18 | } 19 | 20 | /* 21 | Returns a random integer in the range [0, 2^64). 22 | Note: This random number generator is NOT cryptographically secure. 23 | */ 24 | uint64_t RandomUint64() { return RandomUint64(0, std::numeric_limits::max()); } 25 | 26 | private: 27 | std::mt19937 mt_prng_; 28 | }; 29 | 30 | } // namespace starkware 31 | 32 | #endif // STARKWARE_UTILS_PRNG_H_ 33 | -------------------------------------------------------------------------------- /src/starkware/utils/prng_test.cc: -------------------------------------------------------------------------------- 1 | #include "starkware/utils/prng.h" 2 | 3 | #include 4 | 5 | #include "gtest/gtest.h" 6 | 7 | #include "starkware/utils/math.h" 8 | 9 | namespace starkware { 10 | namespace { 11 | 12 | TEST(Prng, PartialRange) { 13 | Prng prng; 14 | uint64_t min_num = std::numeric_limits::max(); 15 | uint64_t max_num = 0; 16 | for (size_t i = 0; i < 1000; i++) { 17 | const uint64_t rnd = prng.RandomUint64(900, 910); 18 | min_num = std::min(min_num, rnd); 19 | max_num = std::max(max_num, rnd); 20 | } 21 | // These should fail with negligible probability (~2^(-137)). 22 | EXPECT_EQ(min_num, 900); 23 | EXPECT_EQ(max_num, 910); 24 | } 25 | 26 | TEST(Prng, FullRange) { 27 | Prng prng; 28 | uint64_t max_num = 0; 29 | for (size_t i = 0; i < 100; i++) { 30 | const uint64_t rnd = prng.RandomUint64(); 31 | max_num = std::max(max_num, rnd); 32 | } 33 | // This should fail with negligible probability (2^(-100)). 34 | EXPECT_GT(max_num, Pow2(63)); 35 | } 36 | 37 | } // namespace 38 | } // namespace starkware 39 | -------------------------------------------------------------------------------- /src/starkware/utils/test_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef STARKWARE_UTILS_TEST_UTILS_H_ 2 | #define STARKWARE_UTILS_TEST_UTILS_H_ 3 | 4 | #include 5 | 6 | #include "gmock/gmock.h" 7 | 8 | #include "starkware/utils/error_handling.h" 9 | 10 | namespace starkware { 11 | 12 | // Below implementation is an altered version of EXPECT_THROW from GTest. 13 | #define EXPECT_ASSERT(statement, expected_matcher) \ 14 | GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ 15 | if (::testing::internal::ConstCharPtr gtest_msg = "") { \ 16 | bool gtest_caught_expected = false; \ 17 | std::string message__; \ 18 | try { \ 19 | GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ 20 | } catch (const StarkwareException& e) { \ 21 | gtest_caught_expected = true; \ 22 | message__ = e.what(); \ 23 | } catch (...) { \ 24 | gtest_msg.value = "Expected: " #statement \ 25 | " throws an exception of type StarkwareException" \ 26 | ".\n Actual: it throws a different type."; \ 27 | goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ 28 | } \ 29 | if (gtest_caught_expected) { \ 30 | EXPECT_THAT(message__, expected_matcher); \ 31 | } else { \ 32 | gtest_msg.value = "Expected: " #statement \ 33 | " throws an exception of type StarkwareException" \ 34 | ".\n Actual: it throws nothing."; \ 35 | goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ 36 | } \ 37 | } else { \ 38 | GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__) \ 39 | : GTEST_NONFATAL_FAILURE_(gtest_msg.value); \ 40 | } 41 | 42 | } // namespace starkware 43 | 44 | #endif // STARKWARE_UTILS_TEST_UTILS_H_ 45 | -------------------------------------------------------------------------------- /src/third_party/gsl/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Martin Moene 4 | Copyright (c) 2015 Microsoft Corporation. All rights reserved. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | --------------------------------------------------------------------------------