├── .clang-format ├── .github └── workflows │ ├── c-cpp.yml │ └── codeql-analysis.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── build.zig └── src ├── blind_rsa.c ├── blind_rsa.h └── test_blind_rsa.c /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveMacros: true 6 | AlignConsecutiveAssignments: true 7 | AlignConsecutiveBitFields: true 8 | AlignConsecutiveDeclarations: true 9 | AlignEscapedNewlines: true 10 | AlignOperands: true 11 | AlignTrailingComments: false 12 | AllowAllArgumentsOnNextLine: true 13 | AllowAllConstructorInitializersOnNextLine: true 14 | AllowAllParametersOfDeclarationOnNextLine: true 15 | AllowShortEnumsOnASingleLine: true 16 | AllowShortBlocksOnASingleLine: false 17 | AllowShortCaseLabelsOnASingleLine: false 18 | AllowShortFunctionsOnASingleLine: false 19 | AllowShortLambdasOnASingleLine: All 20 | AllowShortIfStatementsOnASingleLine: Never 21 | AllowShortLoopsOnASingleLine: false 22 | AlwaysBreakAfterDefinitionReturnType: None 23 | AlwaysBreakAfterReturnType: TopLevelDefinitions 24 | AlwaysBreakBeforeMultilineStrings: true 25 | AlwaysBreakTemplateDeclarations: MultiLine 26 | AttributeMacros: 27 | - __capability 28 | BinPackArguments: true 29 | BinPackParameters: true 30 | BraceWrapping: 31 | AfterCaseLabel: false 32 | AfterClass: false 33 | AfterControlStatement: Never 34 | AfterEnum: false 35 | AfterFunction: true 36 | AfterNamespace: false 37 | AfterObjCDeclaration: false 38 | AfterStruct: false 39 | AfterUnion: false 40 | AfterExternBlock: false 41 | BeforeCatch: false 42 | BeforeElse: false 43 | BeforeLambdaBody: false 44 | BeforeWhile: false 45 | IndentBraces: false 46 | SplitEmptyFunction: true 47 | SplitEmptyRecord: true 48 | SplitEmptyNamespace: true 49 | BreakBeforeBinaryOperators: None 50 | BreakBeforeConceptDeclarations: true 51 | BreakBeforeBraces: WebKit 52 | BreakBeforeInheritanceComma: true 53 | BreakInheritanceList: BeforeColon 54 | BreakBeforeTernaryOperators: true 55 | BreakConstructorInitializersBeforeComma: false 56 | BreakConstructorInitializers: BeforeComma 57 | BreakAfterJavaFieldAnnotations: false 58 | BreakStringLiterals: true 59 | ColumnLimit: 100 60 | CommentPragmas: "^ IWYU pragma:" 61 | CompactNamespaces: false 62 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 63 | ConstructorInitializerIndentWidth: 4 64 | ContinuationIndentWidth: 4 65 | Cpp11BracedListStyle: false 66 | DeriveLineEnding: true 67 | DerivePointerAlignment: true 68 | DisableFormat: false 69 | EmptyLineBeforeAccessModifier: LogicalBlock 70 | ExperimentalAutoDetectBinPacking: true 71 | FixNamespaceComments: false 72 | ForEachMacros: 73 | - foreach 74 | - Q_FOREACH 75 | - BOOST_FOREACH 76 | StatementAttributeLikeMacros: 77 | - Q_EMIT 78 | IncludeBlocks: Preserve 79 | IncludeCategories: 80 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 81 | Priority: 2 82 | SortPriority: 0 83 | CaseSensitive: false 84 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 85 | Priority: 3 86 | SortPriority: 0 87 | CaseSensitive: false 88 | - Regex: ".*" 89 | Priority: 1 90 | SortPriority: 0 91 | CaseSensitive: false 92 | IncludeIsMainRegex: "(Test)?$" 93 | IncludeIsMainSourceRegex: "" 94 | IndentCaseLabels: false 95 | IndentCaseBlocks: false 96 | IndentGotoLabels: true 97 | IndentPPDirectives: AfterHash 98 | IndentExternBlock: AfterExternBlock 99 | IndentRequires: false 100 | IndentWidth: 4 101 | IndentWrappedFunctionNames: false 102 | InsertTrailingCommas: None 103 | JavaScriptQuotes: Leave 104 | JavaScriptWrapImports: true 105 | KeepEmptyLinesAtTheStartOfBlocks: true 106 | MacroBlockBegin: "" 107 | MacroBlockEnd: "" 108 | MaxEmptyLinesToKeep: 1 109 | NamespaceIndentation: Inner 110 | ObjCBinPackProtocolList: Auto 111 | ObjCBlockIndentWidth: 4 112 | ObjCBreakBeforeNestedBlockParam: true 113 | ObjCSpaceAfterProperty: true 114 | ObjCSpaceBeforeProtocolList: true 115 | PenaltyBreakAssignment: 2 116 | PenaltyBreakBeforeFirstCallParameter: 19 117 | PenaltyBreakComment: 300 118 | PenaltyBreakFirstLessLess: 120 119 | PenaltyBreakString: 1000 120 | PenaltyBreakTemplateDeclaration: 10 121 | PenaltyExcessCharacter: 1000000 122 | PenaltyReturnTypeOnItsOwnLine: 60 123 | PenaltyIndentedWhitespace: 0 124 | PointerAlignment: Left 125 | ReflowComments: true 126 | SortIncludes: true 127 | SortJavaStaticImport: Before 128 | SortUsingDeclarations: true 129 | SpaceAfterCStyleCast: true 130 | SpaceAfterLogicalNot: false 131 | SpaceAfterTemplateKeyword: true 132 | SpaceBeforeAssignmentOperators: true 133 | SpaceBeforeCaseColon: false 134 | SpaceBeforeCpp11BracedList: true 135 | SpaceBeforeCtorInitializerColon: true 136 | SpaceBeforeInheritanceColon: true 137 | SpaceBeforeParens: ControlStatements 138 | SpaceAroundPointerQualifiers: Default 139 | SpaceBeforeRangeBasedForLoopColon: true 140 | SpaceInEmptyBlock: true 141 | SpaceInEmptyParentheses: false 142 | SpacesBeforeTrailingComments: 1 143 | SpacesInAngles: false 144 | SpacesInConditionalStatement: false 145 | SpacesInContainerLiterals: true 146 | SpacesInCStyleCastParentheses: false 147 | SpacesInParentheses: false 148 | SpacesInSquareBrackets: false 149 | SpaceBeforeSquareBrackets: false 150 | BitFieldColonSpacing: Both 151 | Standard: Latest 152 | StatementMacros: 153 | - Q_UNUSED 154 | - QT_REQUIRE_VERSION 155 | TabWidth: 4 156 | UseCRLF: false 157 | UseTab: Never 158 | WhitespaceSensitiveMacros: 159 | - STRINGIZE 160 | - PP_STRINGIZE 161 | - BOOST_PP_STRINGIZE 162 | - NS_SWIFT_NAME 163 | - CF_SWIFT_NAME 164 | --- 165 | 166 | -------------------------------------------------------------------------------- /.github/workflows/c-cpp.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: make 17 | run: make 18 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | schedule: 9 | - cron: '45 22 * * 1' 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze 14 | runs-on: ubuntu-latest 15 | 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | language: [ 'cpp' ] 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v2 24 | 25 | - name: Initialize CodeQL 26 | uses: github/codeql-action/init@v1 27 | with: 28 | languages: ${{ matrix.language }} 29 | 30 | - run: | 31 | make 32 | 33 | - name: Perform CodeQL Analysis 34 | uses: github/codeql-action/analyze@v1 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | *~ 55 | -------------------------------------------------------------------------------- /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 [yyyy] [name of copyright owner] 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: test_blind_rsa 2 | 3 | test_blind_rsa: src/blind_rsa.c src/blind_rsa.h src/test_blind_rsa.c 4 | $(CC) -Wall -Isrc -I/opt/homebrew/opt/openssl@3/include -I/usr/local/opt/openssl@3/include -I/usr/local/include -L/opt/homebrew/opt/openssl@3/lib -L/usr/local/opt/openssl@3/lib -L/usr/local/lib -o test src/blind_rsa.c src/test_blind_rsa.c -lcrypto 5 | ./test 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blind RSA signatures 2 | 3 | Author-blinded RSASSA-PSS RSAE signatures. 4 | 5 | This is an implementation of the [RSA Blind Signatures](https://www.rfc-editor.org/rfc/rfc9474.html) RFC, based on [the Zig implementation](https://github.com/jedisct1/zig-rsa-blind-signatures). 6 | 7 | ## Protocol overview 8 | 9 | A client asks a server to sign a message. The server receives the message, and returns the signature. 10 | 11 | Using that `(message, signature)` pair, the client can locally compute a second, valid `(message', signature')` pair. 12 | 13 | Anyone can verify that `(message', signature')` is valid for the server's public key, even though the server didn't see that pair before. 14 | But no one besides the client can link `(message', signature')` to `(message, signature)`. 15 | 16 | Using that scheme, a server can issue a token and verify that a client has a valid token, without being able to link both actions to the same client. 17 | 18 | 1. The client creates a random message, and blinds it with a random, secret factor. 19 | 2. The server receives the blind message, signs it and returns a blind signature. 20 | 3. From the blind signature, and knowing the secret factor, the client can locally compute a `(message, signature)` pair that can be verified using the server's public key. 21 | 4. Anyone, including the server, can thus later verify that `(message, signature)` is valid, without knowing when step 2 occurred. 22 | 23 | The scheme was designed by David Chaum, and was originally implemented for anonymizing DigiCash transactions. 24 | 25 | Random noise must be added to messages that don't include enough entropy. An optional "Message Randomizer" can be used for that purpose. 26 | 27 | ## Dependencies 28 | 29 | This implementation requires OpenSSL (1.1.x or 3.x.y) or BoringSSL. 30 | 31 | ## Usage 32 | 33 | ```c 34 | #include 35 | 36 | // Initialize a context with the default parameters 37 | BRSAContext context; 38 | brsa_context_init_default(&context); 39 | 40 | // [SERVER]: Generate a RSA-2048 key pair 41 | BRSASecretKey sk; 42 | BRSAPublicKey pk; 43 | assert(brsa_keypair_generate(&sk, &pk, 2048) == 0); 44 | 45 | // Noise is not required if the message is random. 46 | // If it is not NULL, it will be automatically filled by brsa_blind_sign(). 47 | BRSAMessageRandomizer *msg_randomizer = NULL; 48 | 49 | // [CLIENT]: create a random message and blind it for the server whose public key is `pk`. 50 | // The client must store the message and the secret. 51 | uint8_t msg[32]; 52 | const size_t msg_len = sizeof msg; 53 | BRSABlindMessage blind_msg; 54 | BRSABlindingSecret client_secret; 55 | assert(brsa_blind_message_generate(&context, &blind_msg, msg, msg_len, &client_secret, &pk) == 56 | 0); 57 | 58 | // [SERVER]: compute a signature for a blind message, to be sent to the client. 59 | // The client secret should not be sent to the server. 60 | BRSABlindSignature blind_sig; 61 | assert(brsa_blind_sign(&context, &blind_sig, &sk, &blind_msg) == 0); 62 | brsa_blind_message_deinit(&blind_msg); 63 | 64 | // [CLIENT]: later, when the client wants to redeem a signed blind message, 65 | // using the blinding secret, it can locally compute the signature of the 66 | // original message. 67 | // The client then owns a new valid (message, signature) pair, and the 68 | // server cannot link it to a previous(blinded message, blind signature) pair. 69 | // Note that the finalization function also verifies that the signature is 70 | // correct for the server public key. 71 | BRSASignature sig; 72 | assert(brsa_finalize( 73 | &context, &sig, &blind_sig, &client_secret, msg_randomizer, &pk, msg, msg_len) == 0); 74 | brsa_blind_signature_deinit(&blind_sig); 75 | brsa_blinding_secret_deinit(&client_secret); 76 | 77 | // [SERVER]: a non-blind signature can be verified using the server's public key. 78 | assert(brsa_verify(&context, &sig, &pk, msg_randomizer, msg, msg_len) == 0); 79 | brsa_signature_deinit(&sig); 80 | 81 | brsa_secretkey_deinit(&sk); 82 | brsa_publickey_deinit(&pk); 83 | ``` 84 | 85 | Deterministic padding is also supported, by creating a context with `brsa_context_init_deterministic()`: 86 | 87 | ```c 88 | // Initialize a context to use deterministic padding 89 | BRSAContext context; 90 | brsa_context_init_deterministic(&context); 91 | ``` 92 | 93 | Most applications should use the default (probabilistic) mode instead. 94 | 95 | A custom hash function and salt length can also be specified with `brsa_context_init_custom()`: 96 | 97 | ```c 98 | // Initialize a context with SHA-256 as a Hash and MGF function, 99 | // and a 48 byte salt. 100 | BRSAContext context; 101 | brsa_context_init_custom(&context, BRSA_SHA256, 48); 102 | ``` 103 | 104 | Some additional helper functions for key management are included: 105 | 106 | ```c 107 | // Get a key identifier 108 | uint8_t key_id[4]; 109 | assert(brsa_publickey_id(&context, key_id, sizeof key_id, &pk) == 0); 110 | 111 | // Key serialization 112 | BRSASerializedKey sk_der, pk_der; 113 | assert(brsa_secretkey_export(&sk_der, &sk) == 0); 114 | assert(brsa_publickey_export(&pk_der, &pk) == 0); 115 | 116 | // Store the SubjectPublicKeyInfo in DER format 117 | BRSASerializedKey spki_der; 118 | assert(brsa_publickey_export_spki(&context, &spki_der, &pk) == 0); 119 | 120 | // Free key resources 121 | brsa_secretkey_deinit(&sk); 122 | brsa_publickey_deinit(&pk); 123 | 124 | // Key deserialization 125 | assert(brsa_secretkey_import(&sk, sk_der.bytes, sk_der.bytes_len) == 0); 126 | assert(brsa_publickey_import(&pk, pk_der.bytes, pk_der.bytes_len) == 0); 127 | brsa_serializedkey_deinit(&sk_der); 128 | brsa_serializedkey_deinit(&pk_der); 129 | ``` 130 | 131 | All these functions return `0` on success and `-1` on error. 132 | 133 | ## For other languages 134 | 135 | * [Zig](https://github.com/jedisct1/zig-blind-rsa-signatures) 136 | * [Rust](https://github.com/jedisct1/rust-blind-rsa-signatures) 137 | * [Go](https://github.com/cloudflare/circl/tree/master/blindsign) 138 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.Build) !void { 4 | const target = b.standardTargetOptions(.{}); 5 | const optimize = b.standardOptimizeOption(.{}); 6 | 7 | const lib_options = b.addOptions(); 8 | const with_boringssl = b.option([]const u8, "with-boringssl", "Path to BoringSSL install") orelse ""; 9 | lib_options.addOption([]const u8, "with-boringssl", with_boringssl); 10 | 11 | const lib = b.addStaticLibrary(.{ 12 | .name = "brsa", 13 | .target = target, 14 | .optimize = optimize, 15 | }); 16 | 17 | lib.linkLibC(); 18 | if (with_boringssl.len > 0) { 19 | var buf_include: [std.posix.PATH_MAX]u8 = undefined; 20 | var buf_include_alloc = std.heap.FixedBufferAllocator.init(&buf_include); 21 | const path_include = try std.fs.path.join(buf_include_alloc.allocator(), &.{ with_boringssl, "include" }); 22 | 23 | var buf_lib: [std.posix.PATH_MAX]u8 = undefined; 24 | var buf_lib_alloc = std.heap.FixedBufferAllocator.init(&buf_lib); 25 | const path_lib = try std.fs.path.join(buf_lib_alloc.allocator(), &.{ with_boringssl, "lib" }); 26 | 27 | lib.addIncludePath(b.path(path_include)); 28 | lib.addLibraryPath(b.path(path_lib)); 29 | } else { 30 | lib.addIncludePath(.{ .cwd_relative = "/opt/homebrew/opt/openssl@3/include" }); 31 | lib.addLibraryPath(.{ .cwd_relative = "/opt/homebrew/opt/openssl@3/lib" }); 32 | } 33 | lib.linkSystemLibrary("crypto"); 34 | 35 | const lib_source_files = &.{"src/blind_rsa.c"}; 36 | 37 | lib.addCSourceFiles(.{ .files = lib_source_files }); 38 | b.installArtifact(lib); 39 | 40 | const exe_source_files = &.{"src/test_blind_rsa.c"}; 41 | 42 | const exe = b.addExecutable(.{ 43 | .name = "c-blind-rsa-signatures", 44 | .target = target, 45 | .optimize = optimize, 46 | }); 47 | 48 | if (with_boringssl.len > 0) { 49 | var buf_include: [std.posix.PATH_MAX]u8 = undefined; 50 | var buf_include_alloc = std.heap.FixedBufferAllocator.init(&buf_include); 51 | const path_include = try std.fs.path.join(buf_include_alloc.allocator(), &.{ with_boringssl, "include" }); 52 | 53 | var buf_lib: [std.posix.PATH_MAX]u8 = undefined; 54 | var buf_lib_alloc = std.heap.FixedBufferAllocator.init(&buf_lib); 55 | const path_lib = try std.fs.path.join(buf_lib_alloc.allocator(), &.{ with_boringssl, "lib" }); 56 | 57 | exe.addIncludePath(b.path(path_include)); 58 | exe.addLibraryPath(b.path(path_lib)); 59 | } else { 60 | exe.addIncludePath(.{ .cwd_relative = "/opt/homebrew/opt/openssl@3/include" }); 61 | exe.addLibraryPath(.{ .cwd_relative = "/opt/homebrew/opt/openssl@3/lib" }); 62 | } 63 | exe.addCSourceFiles(.{ .files = exe_source_files }); 64 | exe.linkLibrary(lib); 65 | exe.linkLibC(); 66 | 67 | const run_cmd = b.addRunArtifact(exe); 68 | 69 | run_cmd.step.dependOn(b.getInstallStep()); 70 | 71 | if (b.args) |args| { 72 | run_cmd.addArgs(args); 73 | } 74 | 75 | const run_exe_unit_tests = b.addRunArtifact(exe); 76 | 77 | const test_step = b.step("test", "Run unit tests"); 78 | test_step.dependOn(&run_exe_unit_tests.step); 79 | } 80 | -------------------------------------------------------------------------------- /src/blind_rsa.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #ifndef OPENSSL_API_COMPAT 7 | # define OPENSSL_API_COMPAT 10100 8 | #endif 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "blind_rsa.h" 20 | 21 | #ifndef OPENSSL_IS_BORINGSSL 22 | # define BN_bn2bin_padded(OUT, LEN, IN) (BN_bn2binpad((IN), (OUT), (LEN)) == (size_t) (LEN)) 23 | #endif 24 | 25 | #define MIN_MODULUS_BITS 2048 26 | #define MAX_MODULUS_BITS 4096 27 | #define MAX_SERIALIZED_PK_LEN 1000 28 | 29 | #define MAX_HASH_DIGEST_LENGTH EVP_MAX_MD_SIZE 30 | 31 | static int 32 | _rsa_bits(const EVP_PKEY *evp_pkey) 33 | { 34 | #if OPENSSL_VERSION_MAJOR >= 3 35 | return EVP_PKEY_get_bits(evp_pkey); 36 | #else 37 | return RSA_bits(EVP_PKEY_get0_RSA((EVP_PKEY *) evp_pkey)); 38 | #endif 39 | } 40 | 41 | static size_t 42 | _rsa_size(const EVP_PKEY *evp_pkey) 43 | { 44 | #if OPENSSL_VERSION_MAJOR >= 3 45 | return EVP_PKEY_get_size(evp_pkey); 46 | #else 47 | return (size_t) RSA_size(EVP_PKEY_get0_RSA((EVP_PKEY *) evp_pkey)); 48 | #endif 49 | } 50 | 51 | static BIGNUM * 52 | _rsa_n(const EVP_PKEY *evp_pkey) 53 | { 54 | #if OPENSSL_VERSION_MAJOR >= 3 55 | BIGNUM *bn = NULL; 56 | EVP_PKEY_get_bn_param(evp_pkey, "n", &bn); 57 | return bn; 58 | #else 59 | return BN_dup(RSA_get0_n(EVP_PKEY_get0_RSA((EVP_PKEY *) evp_pkey))); 60 | #endif 61 | } 62 | 63 | static BIGNUM * 64 | _rsa_e(const EVP_PKEY *evp_pkey) 65 | { 66 | #if OPENSSL_VERSION_MAJOR >= 3 67 | BIGNUM *bn = NULL; 68 | EVP_PKEY_get_bn_param(evp_pkey, "e", &bn); 69 | return bn; 70 | #else 71 | return BN_dup(RSA_get0_e(EVP_PKEY_get0_RSA((EVP_PKEY *) evp_pkey))); 72 | #endif 73 | } 74 | 75 | static BN_MONT_CTX * 76 | new_mont_domain(const BIGNUM *n) 77 | { 78 | BN_MONT_CTX *mont_ctx = BN_MONT_CTX_new(); 79 | if (mont_ctx == NULL) { 80 | return NULL; 81 | } 82 | BN_CTX *bn_ctx = BN_CTX_new(); 83 | if (bn_ctx == NULL) { 84 | return NULL; 85 | } 86 | BN_CTX_start(bn_ctx); 87 | const int ret = BN_MONT_CTX_set(mont_ctx, n, bn_ctx); 88 | if (ret != ERR_LIB_NONE) { 89 | mont_ctx = NULL; 90 | } 91 | BN_CTX_end(bn_ctx); 92 | BN_CTX_free(bn_ctx); 93 | 94 | return mont_ctx; 95 | } 96 | 97 | void 98 | brsa_context_init_default(BRSAContext *context) 99 | { 100 | brsa_context_init_custom(context, BRSA_SHA384, BRSA_DEFAULT_SALT_LENGTH); 101 | } 102 | 103 | void 104 | brsa_context_init_deterministic(BRSAContext *context) 105 | { 106 | brsa_context_init_custom(context, BRSA_SHA384, 0); 107 | } 108 | 109 | int 110 | brsa_context_init_custom(BRSAContext *context, BRSAHashFunction hash_function, size_t salt_len) 111 | { 112 | const EVP_MD *evp_md; 113 | 114 | switch (hash_function) { 115 | case BRSA_SHA256: 116 | evp_md = EVP_sha256(); 117 | break; 118 | case BRSA_SHA384: 119 | evp_md = EVP_sha384(); 120 | break; 121 | case BRSA_SHA512: 122 | evp_md = EVP_sha512(); 123 | break; 124 | default: 125 | return -1; 126 | } 127 | context->evp_md = evp_md; 128 | if (salt_len == BRSA_DEFAULT_SALT_LENGTH) { 129 | context->salt_len = (size_t) EVP_MD_size(evp_md); 130 | } else { 131 | context->salt_len = salt_len; 132 | } 133 | return 0; 134 | } 135 | 136 | int 137 | brsa_publickey_recover(BRSAPublicKey *pk, const BRSASecretKey *sk) 138 | { 139 | BRSASerializedKey serialized = { .bytes = NULL, .bytes_len = 0 }; 140 | 141 | int ret = i2d_PublicKey(sk->evp_pkey, &serialized.bytes); 142 | if (ret <= 0) { 143 | return -1; 144 | } 145 | serialized.bytes_len = (size_t) ret; 146 | 147 | ret = brsa_publickey_import(pk, serialized.bytes, serialized.bytes_len); 148 | brsa_serializedkey_deinit(&serialized); 149 | 150 | return ret; 151 | } 152 | 153 | int 154 | brsa_keypair_generate(BRSASecretKey *sk, BRSAPublicKey *pk, int modulus_bits) 155 | { 156 | sk->evp_pkey = NULL; 157 | 158 | if (pk != NULL) { 159 | pk->evp_pkey = NULL; 160 | pk->mont_ctx = NULL; 161 | } 162 | 163 | RSA *rsa = RSA_new(); 164 | if (rsa == NULL) { 165 | return -1; 166 | } 167 | 168 | BIGNUM *e = BN_new(); 169 | if (e == NULL) { 170 | RSA_free(rsa); 171 | return -1; 172 | } 173 | BN_set_word(e, RSA_F4); 174 | if (RSA_generate_key_ex(rsa, modulus_bits, e, NULL) != ERR_LIB_NONE) { 175 | RSA_free(rsa); 176 | BN_free(e); 177 | return -1; 178 | } 179 | BN_free(e); 180 | 181 | if ((sk->evp_pkey = EVP_PKEY_new()) == NULL) { 182 | RSA_free(rsa); 183 | return -1; 184 | } 185 | EVP_PKEY_assign(sk->evp_pkey, EVP_PKEY_RSA, rsa); 186 | 187 | if (pk != NULL) { 188 | return brsa_publickey_recover(pk, sk); 189 | } 190 | return 0; 191 | } 192 | 193 | int 194 | brsa_secretkey_import(BRSASecretKey *sk, const uint8_t *der, const size_t der_len) 195 | { 196 | const uint8_t *der_ = der; 197 | 198 | sk->evp_pkey = NULL; 199 | if (der_len > LONG_MAX) { 200 | return -1; 201 | } 202 | if (d2i_PrivateKey(EVP_PKEY_RSA, &sk->evp_pkey, &der_, (long) der_len) == NULL) { 203 | return -1; 204 | } 205 | return 0; 206 | } 207 | 208 | int 209 | brsa_secretkey_export(BRSASerializedKey *serialized, const BRSASecretKey *sk) 210 | { 211 | serialized->bytes = NULL; 212 | 213 | const int ret = i2d_PrivateKey(sk->evp_pkey, &serialized->bytes); 214 | if (ret <= 0) { 215 | return -1; 216 | } 217 | serialized->bytes_len = (size_t) ret; 218 | 219 | return 0; 220 | } 221 | 222 | static int 223 | _rsa_parameters_check(const EVP_PKEY *evp_pkey) 224 | { 225 | const int modulus_bits = _rsa_bits(evp_pkey); 226 | 227 | if (modulus_bits < MIN_MODULUS_BITS) { 228 | ERR_put_error(ERR_LIB_RSA, 0, RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY, __FILE__, __LINE__); 229 | return -1; 230 | } 231 | if (modulus_bits > MAX_MODULUS_BITS) { 232 | ERR_put_error(ERR_LIB_RSA, 0, RSA_R_MODULUS_TOO_LARGE, __FILE__, __LINE__); 233 | return -1; 234 | } 235 | 236 | BIGNUM *e3 = BN_new(); 237 | if (e3 == NULL) { 238 | return -1; 239 | } 240 | BIGNUM *ef4 = BN_new(); 241 | if (ef4 == NULL) { 242 | BN_free(e3); 243 | return -1; 244 | } 245 | BN_set_word(e3, RSA_3); 246 | BN_set_word(ef4, RSA_F4); 247 | int ret = -1; 248 | 249 | BIGNUM *e = _rsa_e(evp_pkey); 250 | if (e != NULL && (BN_cmp(e, e3) == 0 || BN_cmp(e, ef4) == 0)) { 251 | ret = 0; 252 | } 253 | BN_free(e); 254 | BN_free(ef4); 255 | BN_free(e3); 256 | 257 | return ret; 258 | } 259 | 260 | int 261 | brsa_publickey_import(BRSAPublicKey *pk, const uint8_t *der, const size_t der_len) 262 | { 263 | const uint8_t *der_ = der; 264 | 265 | pk->evp_pkey = NULL; 266 | pk->mont_ctx = NULL; 267 | if (der_len > MAX_SERIALIZED_PK_LEN) { 268 | return -1; 269 | } 270 | if (d2i_PublicKey(EVP_PKEY_RSA, &pk->evp_pkey, &der_, (long) der_len) == NULL) { 271 | return -1; 272 | } 273 | pk->mont_ctx = NULL; 274 | if (pk->evp_pkey == NULL) { 275 | brsa_publickey_deinit(pk); 276 | return -1; 277 | } 278 | if (_rsa_parameters_check(pk->evp_pkey) != 0) { 279 | brsa_publickey_deinit(pk); 280 | return -1; 281 | } 282 | BIGNUM *n = _rsa_n(pk->evp_pkey); 283 | if (n == NULL) { 284 | brsa_publickey_deinit(pk); 285 | return -1; 286 | } 287 | pk->mont_ctx = new_mont_domain(n); 288 | BN_free(n); 289 | if (pk->mont_ctx == NULL) { 290 | brsa_publickey_deinit(pk); 291 | return -1; 292 | } 293 | return 0; 294 | } 295 | 296 | int 297 | brsa_publickey_export(BRSASerializedKey *serialized, const BRSAPublicKey *pk) 298 | { 299 | serialized->bytes = NULL; 300 | serialized->bytes_len = 0; 301 | 302 | const int ret = i2d_PublicKey(pk->evp_pkey, &serialized->bytes); 303 | if (ret <= 0) { 304 | return -1; 305 | } 306 | serialized->bytes_len = (size_t) ret; 307 | 308 | return 0; 309 | } 310 | 311 | void 312 | brsa_secretkey_deinit(BRSASecretKey *sk) 313 | { 314 | EVP_PKEY_free(sk->evp_pkey); 315 | sk->evp_pkey = NULL; 316 | } 317 | 318 | void 319 | brsa_publickey_deinit(BRSAPublicKey *pk) 320 | { 321 | EVP_PKEY_free(pk->evp_pkey); 322 | pk->evp_pkey = NULL; 323 | BN_MONT_CTX_free(pk->mont_ctx); 324 | pk->mont_ctx = NULL; 325 | } 326 | 327 | void 328 | brsa_serializedkey_deinit(BRSASerializedKey *serialized) 329 | { 330 | OPENSSL_clear_free(serialized->bytes, serialized->bytes_len); 331 | serialized->bytes = NULL; 332 | } 333 | 334 | void 335 | brsa_blind_message_deinit(BRSABlindMessage *blind_message) 336 | { 337 | OPENSSL_clear_free(blind_message->blind_message, blind_message->blind_message_len); 338 | blind_message->blind_message = NULL; 339 | } 340 | 341 | static int 342 | brsa_blind_message_init(BRSABlindMessage *blind_message, size_t modulus_bytes) 343 | { 344 | blind_message->blind_message_len = modulus_bytes; 345 | if ((blind_message->blind_message = OPENSSL_malloc(blind_message->blind_message_len)) == NULL) { 346 | brsa_blind_message_deinit(blind_message); 347 | return -1; 348 | } 349 | return 0; 350 | } 351 | 352 | void 353 | brsa_blinding_secret_deinit(BRSABlindingSecret *secret) 354 | { 355 | OPENSSL_clear_free(secret->secret, secret->secret_len); 356 | secret->secret = NULL; 357 | } 358 | 359 | static int 360 | brsa_blinding_secret_init(BRSABlindingSecret *secret, size_t modulus_bytes) 361 | { 362 | secret->secret_len = modulus_bytes; 363 | if ((secret->secret = OPENSSL_malloc(secret->secret_len)) == NULL) { 364 | brsa_blinding_secret_deinit(secret); 365 | return -1; 366 | } 367 | return 0; 368 | } 369 | 370 | void 371 | brsa_blind_signature_deinit(BRSABlindSignature *blind_sig) 372 | { 373 | OPENSSL_free(blind_sig->blind_sig); 374 | blind_sig->blind_sig = NULL; 375 | } 376 | 377 | static int 378 | brsa_blind_signature_init(BRSABlindSignature *blind_sig, size_t blind_sig_len) 379 | { 380 | blind_sig->blind_sig_len = blind_sig_len; 381 | if ((blind_sig->blind_sig = OPENSSL_malloc(blind_sig->blind_sig_len)) == NULL) { 382 | brsa_blind_signature_deinit(blind_sig); 383 | return -1; 384 | } 385 | return 0; 386 | } 387 | 388 | void 389 | brsa_signature_deinit(BRSASignature *sig) 390 | { 391 | OPENSSL_free(sig->sig); 392 | sig->sig = NULL; 393 | } 394 | 395 | static int 396 | brsa_signature_init(BRSASignature *sig, size_t sig_len) 397 | { 398 | sig->sig_len = sig_len; 399 | if ((sig->sig = OPENSSL_malloc(sig->sig_len)) == NULL) { 400 | brsa_signature_deinit(sig); 401 | return -1; 402 | } 403 | return 0; 404 | } 405 | 406 | static int 407 | _hash(const EVP_MD *evp_md, const BRSAMessageRandomizer *prefix, uint8_t *msg_hash, 408 | const size_t msg_hash_len, const uint8_t *msg, const size_t msg_len) 409 | { 410 | EVP_MD_CTX *hash_ctx; 411 | 412 | if (msg_hash_len < (size_t) EVP_MD_size(evp_md)) { 413 | return -1; 414 | } 415 | if ((hash_ctx = EVP_MD_CTX_new()) == NULL) { 416 | return -1; 417 | } 418 | int ret = -1; 419 | do { 420 | if (EVP_DigestInit(hash_ctx, evp_md) != ERR_LIB_NONE) { 421 | break; 422 | } 423 | if (prefix != NULL && EVP_DigestUpdate(hash_ctx, msg, msg_len) != ERR_LIB_NONE) { 424 | break; 425 | } 426 | if (EVP_DigestUpdate(hash_ctx, msg, msg_len) != ERR_LIB_NONE || 427 | EVP_DigestFinal_ex(hash_ctx, msg_hash, NULL) != ERR_LIB_NONE) { 428 | break; 429 | } 430 | ret = 0; 431 | } while (0); 432 | EVP_MD_CTX_free(hash_ctx); 433 | 434 | return ret; 435 | } 436 | 437 | static int 438 | _blind(BRSABlindMessage *blind_message, BRSABlindingSecret *secret_, BRSAPublicKey *pk, 439 | BN_CTX *bn_ctx, const uint8_t *padded, size_t padded_len) 440 | { 441 | BIGNUM *m = BN_CTX_get(bn_ctx); 442 | if (BN_bin2bn(padded, padded_len, m) == NULL) { 443 | return -1; 444 | } 445 | BIGNUM *n = _rsa_n(pk->evp_pkey); 446 | if (n == NULL) { 447 | return -1; 448 | } 449 | 450 | // Check that gcd(m, n) == 1 451 | BIGNUM *gcd = BN_CTX_get(bn_ctx); 452 | if (gcd == NULL) { 453 | BN_free(n); 454 | return -1; 455 | } 456 | BN_gcd(gcd, m, n, bn_ctx); 457 | if (BN_is_one(gcd) == 0) { 458 | BN_free(n); 459 | return -1; 460 | } 461 | 462 | // Compute a blind factor and its inverse 463 | 464 | BIGNUM *secret_inv = BN_CTX_get(bn_ctx); 465 | BIGNUM *secret = BN_CTX_get(bn_ctx); 466 | if (secret_inv == NULL || secret == NULL) { 467 | BN_free(n); 468 | return -1; 469 | } 470 | do { 471 | if (BN_rand_range(secret_inv, n) != ERR_LIB_NONE) { 472 | BN_free(n); 473 | return -1; 474 | } 475 | } while (BN_is_one(secret_inv) || BN_mod_inverse(secret, secret_inv, n, bn_ctx) == NULL); 476 | 477 | // Blind the message 478 | 479 | BIGNUM *x = BN_CTX_get(bn_ctx); 480 | BIGNUM *blind_m = BN_CTX_get(bn_ctx); 481 | if (x == NULL || blind_m == NULL) { 482 | BN_free(n); 483 | return -1; 484 | } 485 | BIGNUM *e = _rsa_e(pk->evp_pkey); 486 | if (e == NULL) { 487 | BN_free(n); 488 | return -1; 489 | } 490 | if (BN_mod_exp_mont(x, secret_inv, e, n, bn_ctx, pk->mont_ctx) != ERR_LIB_NONE) { 491 | BN_free(e); 492 | BN_free(n); 493 | return -1; 494 | } 495 | BN_free(e); 496 | BN_clear(secret_inv); 497 | if (BN_mod_mul(blind_m, m, x, n, bn_ctx) != ERR_LIB_NONE) { 498 | BN_free(n); 499 | return -1; 500 | } 501 | BN_free(n); 502 | 503 | // Serialize the blind message 504 | 505 | const size_t modulus_bytes = _rsa_size(pk->evp_pkey); 506 | if (brsa_blind_message_init(blind_message, modulus_bytes) != 0) { 507 | return -1; 508 | } 509 | if (brsa_blinding_secret_init(secret_, modulus_bytes) != 0) { 510 | return -1; 511 | } 512 | if (BN_bn2bin_padded(blind_message->blind_message, (int) blind_message->blind_message_len, 513 | blind_m) != ERR_LIB_NONE) { 514 | return -1; 515 | } 516 | if (BN_bn2bin_padded(secret_->secret, (int) secret_->secret_len, secret) != ERR_LIB_NONE) { 517 | return -1; 518 | } 519 | return 0; 520 | } 521 | 522 | static int 523 | _check_canonical(const BRSASecretKey *sk, const BRSABlindMessage *blind_message) 524 | { 525 | const EVP_PKEY *evp_pkey = sk->evp_pkey; 526 | const size_t modulus_bytes = _rsa_size(evp_pkey); 527 | if (blind_message->blind_message_len != modulus_bytes) { 528 | ERR_put_error(ERR_LIB_RSA, 0, RSA_R_DATA_TOO_LARGE_FOR_MODULUS, __FILE__, __LINE__); 529 | return -1; 530 | } 531 | 532 | BIGNUM *n; 533 | unsigned char *n_s; 534 | if ((n = _rsa_n(evp_pkey)) == NULL) { 535 | return -1; 536 | } 537 | if ((n_s = OPENSSL_malloc(modulus_bytes)) == NULL) { 538 | BN_free(n); 539 | return -1; 540 | } 541 | if (BN_bn2bin_padded(n_s, (int) modulus_bytes, n) != ERR_LIB_NONE) { 542 | OPENSSL_free(n_s); 543 | BN_free(n); 544 | return -1; 545 | } 546 | BN_free(n); 547 | size_t i; 548 | for (i = 0; i < modulus_bytes; i++) { 549 | const uint8_t a = blind_message->blind_message[i]; 550 | const uint8_t b = n_s[i]; 551 | if (a < b) { 552 | break; 553 | } 554 | if (a > b || i + 1 == modulus_bytes) { 555 | OPENSSL_free(n_s); 556 | ERR_put_error(ERR_LIB_RSA, 0, RSA_R_DATA_TOO_LARGE_FOR_MODULUS, __FILE__, __LINE__); 557 | return -1; 558 | } 559 | } 560 | OPENSSL_free(n_s); 561 | return 0; 562 | } 563 | 564 | int 565 | brsa_blind(const BRSAContext *context, BRSABlindMessage *blind_message, BRSABlindingSecret *secret, 566 | BRSAMessageRandomizer *msg_randomizer, BRSAPublicKey *pk, const uint8_t *msg, 567 | size_t msg_len) 568 | { 569 | if (_rsa_parameters_check(pk->evp_pkey) != 0) { 570 | return -1; 571 | } 572 | const size_t modulus_bytes = _rsa_size(pk->evp_pkey); 573 | 574 | // Compute H(msg) 575 | 576 | if (msg_randomizer != NULL) { 577 | if (RAND_bytes(msg_randomizer->noise, sizeof msg_randomizer->noise) != ERR_LIB_NONE) { 578 | return -1; 579 | } 580 | } 581 | 582 | uint8_t msg_hash[MAX_HASH_DIGEST_LENGTH]; 583 | if (_hash(context->evp_md, msg_randomizer, msg_hash, sizeof msg_hash, msg, msg_len) != 0) { 584 | return -1; 585 | } 586 | 587 | // PSS-MGF1 padding 588 | 589 | const size_t padded_len = modulus_bytes; 590 | uint8_t *padded = OPENSSL_malloc(padded_len); 591 | if (padded == NULL) { 592 | return -1; 593 | } 594 | 595 | const EVP_MD *evp_md = context->evp_md; 596 | if (RSA_padding_add_PKCS1_PSS_mgf1((RSA *) EVP_PKEY_get0_RSA(pk->evp_pkey), padded, msg_hash, 597 | evp_md, evp_md, context->salt_len) != ERR_LIB_NONE) { 598 | return -1; 599 | } 600 | OPENSSL_cleanse(msg_hash, sizeof msg_hash); 601 | 602 | // Blind the padded message 603 | 604 | BN_CTX *bn_ctx = BN_CTX_new(); 605 | if (bn_ctx == NULL) { 606 | return -1; 607 | } 608 | BN_CTX_start(bn_ctx); 609 | 610 | const int ret = _blind(blind_message, secret, pk, bn_ctx, padded, padded_len); 611 | 612 | BN_CTX_end(bn_ctx); 613 | BN_CTX_free(bn_ctx); 614 | OPENSSL_clear_free(padded, padded_len); 615 | 616 | return ret; 617 | } 618 | 619 | int 620 | brsa_blind_message_generate(const BRSAContext *context, BRSABlindMessage *blind_message, 621 | uint8_t *msg, size_t msg_len, BRSABlindingSecret *secret, 622 | BRSAPublicKey *pk) 623 | { 624 | if (RAND_bytes(msg, msg_len) != ERR_LIB_NONE) { 625 | return -1; 626 | } 627 | return brsa_blind(context, blind_message, secret, NULL, pk, msg, msg_len); 628 | } 629 | 630 | int 631 | brsa_blind_sign(const BRSAContext *context, BRSABlindSignature *blind_sig, BRSASecretKey *sk, 632 | const BRSABlindMessage *blind_message) 633 | { 634 | (void) context; 635 | 636 | if (_rsa_parameters_check(sk->evp_pkey) != 0) { 637 | return -1; 638 | } 639 | if (_check_canonical(sk, blind_message) != 0) { 640 | return -1; 641 | } 642 | 643 | if (brsa_blind_signature_init(blind_sig, _rsa_size(sk->evp_pkey)) != 0) { 644 | return -1; 645 | } 646 | if (RSA_private_encrypt(blind_sig->blind_sig_len, blind_message->blind_message, 647 | blind_sig->blind_sig, (RSA *) EVP_PKEY_get0_RSA(sk->evp_pkey), 648 | RSA_NO_PADDING) < 0) { 649 | brsa_blind_signature_deinit(blind_sig); 650 | return -1; 651 | } 652 | return 0; 653 | } 654 | 655 | static int 656 | rsassa_pss_verify(const BRSAContext *context, const BRSASignature *sig, BRSAPublicKey *pk, 657 | const BRSAMessageRandomizer *msg_randomizer, const uint8_t *msg, 658 | const size_t msg_len) 659 | { 660 | const size_t modulus_bytes = _rsa_size(pk->evp_pkey); 661 | if (sig->sig_len != modulus_bytes) { 662 | ERR_put_error(ERR_LIB_RSA, 0, RSA_R_DATA_TOO_LARGE_FOR_MODULUS, __FILE__, __LINE__); 663 | return -1; 664 | } 665 | 666 | uint8_t msg_hash[MAX_HASH_DIGEST_LENGTH]; 667 | if (_hash(context->evp_md, msg_randomizer, msg_hash, sizeof msg_hash, msg, msg_len) != 0) { 668 | return -1; 669 | } 670 | 671 | const size_t em_len = sig->sig_len; 672 | uint8_t *em = OPENSSL_malloc(em_len); 673 | if (em == NULL) { 674 | return -1; 675 | } 676 | 677 | if (RSA_public_decrypt(sig->sig_len, sig->sig, em, (RSA *) EVP_PKEY_get0_RSA(pk->evp_pkey), 678 | RSA_NO_PADDING) < 0) { 679 | OPENSSL_free(em); 680 | return -1; 681 | } 682 | 683 | const EVP_MD *evp_md = context->evp_md; 684 | if (RSA_verify_PKCS1_PSS_mgf1((RSA *) EVP_PKEY_get0_RSA(pk->evp_pkey), msg_hash, evp_md, evp_md, 685 | em, context->salt_len) != ERR_LIB_NONE) { 686 | OPENSSL_free(em); 687 | return -1; 688 | } 689 | OPENSSL_free(em); 690 | return 0; 691 | } 692 | 693 | static int 694 | _finalize(const BRSAContext *context, BRSASignature *sig, const BRSABlindSignature *blind_sig, 695 | const BRSABlindingSecret *secret_, const BRSAMessageRandomizer *msg_randomizer, 696 | BRSAPublicKey *pk, BN_CTX *bn_ctx, const uint8_t *msg, size_t msg_len) 697 | { 698 | BIGNUM *secret = BN_CTX_get(bn_ctx); 699 | BIGNUM *blind_z = BN_CTX_get(bn_ctx); 700 | BIGNUM *z = BN_CTX_get(bn_ctx); 701 | if (secret == NULL || blind_z == NULL || z == NULL) { 702 | return -1; 703 | } 704 | if (BN_bin2bn(secret_->secret, secret_->secret_len, secret) == NULL) { 705 | return -1; 706 | } 707 | if (BN_bin2bn(blind_sig->blind_sig, blind_sig->blind_sig_len, blind_z) == NULL) { 708 | return -1; 709 | } 710 | 711 | BIGNUM *n = _rsa_n(pk->evp_pkey); 712 | if (n == NULL) { 713 | return -1; 714 | } 715 | if (BN_mod_mul(z, blind_z, secret, n, bn_ctx) != ERR_LIB_NONE) { 716 | BN_free(n); 717 | return -1; 718 | } 719 | BN_free(n); 720 | 721 | if (brsa_signature_init(sig, _rsa_size(pk->evp_pkey)) != 0) { 722 | return -1; 723 | } 724 | if (BN_bn2bin_padded(sig->sig, (int) sig->sig_len, z) != ERR_LIB_NONE) { 725 | brsa_signature_deinit(sig); 726 | return -1; 727 | } 728 | if (rsassa_pss_verify(context, sig, pk, msg_randomizer, msg, msg_len) != 0) { 729 | brsa_signature_deinit(sig); 730 | return -1; 731 | } 732 | return 0; 733 | } 734 | 735 | int 736 | brsa_finalize(const BRSAContext *context, BRSASignature *sig, const BRSABlindSignature *blind_sig, 737 | const BRSABlindingSecret *secret, const BRSAMessageRandomizer *msg_randomizer, 738 | BRSAPublicKey *pk, const uint8_t *msg, size_t msg_len) 739 | { 740 | if (_rsa_parameters_check(pk->evp_pkey) != 0) { 741 | return -1; 742 | } 743 | const size_t modulus_bytes = _rsa_size(pk->evp_pkey); 744 | if (blind_sig->blind_sig_len != modulus_bytes || secret->secret_len != modulus_bytes) { 745 | ERR_put_error(ERR_LIB_RSA, 0, RSA_R_DATA_TOO_LARGE_FOR_MODULUS, __FILE__, __LINE__); 746 | return -1; 747 | } 748 | 749 | BN_CTX *bn_ctx = BN_CTX_new(); 750 | if (bn_ctx == NULL) { 751 | return -1; 752 | } 753 | BN_CTX_start(bn_ctx); 754 | 755 | const int ret = 756 | _finalize(context, sig, blind_sig, secret, msg_randomizer, pk, bn_ctx, msg, msg_len); 757 | 758 | BN_CTX_end(bn_ctx); 759 | BN_CTX_free(bn_ctx); 760 | 761 | return ret; 762 | } 763 | 764 | int 765 | brsa_verify(const BRSAContext *context, const BRSASignature *sig, BRSAPublicKey *pk, 766 | const BRSAMessageRandomizer *msg_randomizer, const uint8_t *msg, size_t msg_len) 767 | { 768 | return rsassa_pss_verify(context, sig, pk, msg_randomizer, msg, msg_len); 769 | } 770 | 771 | static const unsigned char rsassa_pss_s_template[] = { 772 | // clang-format off 773 | #define SEQ 0x30 774 | #define EXT 0x80 775 | #define CON 0xa0 776 | #define INT 0x02 777 | #define BIT 0x03 778 | #define OBJ 0x06 779 | 780 | SEQ, EXT | 2, 0, 0, // container length - offset 2 781 | SEQ, 61, // Algorithm sequence 782 | OBJ, 9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a, // Signature algorithm (RSASSA-PSS) 783 | SEQ, 48, // RSASSA-PSS parameters sequence 784 | CON | 0, 2 + 2 + 9, 785 | SEQ, 2 + 9, OBJ, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Hash function - offset 21 786 | 787 | CON | 1, 2 + 24, 788 | SEQ, 24, OBJ, 9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, // Padding function (MGF1) and parameters 789 | SEQ, 2 + 9, OBJ, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, // MGF1 hash function - offset 49 790 | 791 | CON | 2, 2 + 1, INT, 1, 0, // Salt length - offset 66 792 | BIT, EXT | 2, 0, 0, // Public key length - Bit string - offset 69 793 | 0 // No partial bytes 794 | }; // clang-format on 795 | 796 | int 797 | brsa_publickey_export_spki(const BRSAContext *context, BRSASerializedKey *spki, 798 | const BRSAPublicKey *pk) 799 | { 800 | spki->bytes = NULL; 801 | spki->bytes_len = 0; 802 | 803 | BRSASerializedKey spki_raw = { 804 | .bytes = NULL, 805 | .bytes_len = 0, 806 | }; 807 | int ret = i2d_PublicKey(pk->evp_pkey, &spki_raw.bytes); 808 | if (ret <= 0) { 809 | return -1; 810 | } 811 | spki_raw.bytes_len = (size_t) ret; 812 | 813 | const size_t template_len = sizeof rsassa_pss_s_template; 814 | const size_t container_len = template_len - 4 + spki_raw.bytes_len; 815 | unsigned char *spki_bytes = OPENSSL_malloc(template_len + spki_raw.bytes_len); 816 | if (spki_bytes == NULL) { 817 | brsa_serializedkey_deinit(&spki_raw); 818 | return -1; 819 | } 820 | memcpy(spki_bytes, rsassa_pss_s_template, template_len); 821 | memcpy(&spki_bytes[template_len], spki_raw.bytes, spki_raw.bytes_len); 822 | spki_bytes[2] = (unsigned char) (container_len >> 8); 823 | spki_bytes[3] = (unsigned char) (container_len & 0xff); 824 | spki_bytes[66] = (unsigned char) (context->salt_len & 0xff); 825 | spki_bytes[69] = (unsigned char) ((1 + spki_raw.bytes_len) >> 8); 826 | spki_bytes[70] = (unsigned char) ((1 + spki_raw.bytes_len) & 0xff); 827 | brsa_serializedkey_deinit(&spki_raw); 828 | 829 | X509_ALGOR *algor_mgf1 = X509_ALGOR_new(); 830 | if (algor_mgf1 == NULL) { 831 | OPENSSL_free(spki_bytes); 832 | return -1; 833 | } 834 | X509_ALGOR_set_md(algor_mgf1, context->evp_md); 835 | ASN1_STRING *algor_mgf1_s = ASN1_STRING_new(); 836 | if (algor_mgf1_s == NULL) { 837 | OPENSSL_free(spki_bytes); 838 | X509_ALGOR_free(algor_mgf1); 839 | return -1; 840 | } 841 | ASN1_item_pack(algor_mgf1, ASN1_ITEM_rptr(X509_ALGOR), &algor_mgf1_s); 842 | X509_ALGOR_free(algor_mgf1); 843 | 844 | unsigned char mgf1_s_data[2 + 2 + 9]; 845 | const unsigned char *alg_data = ASN1_STRING_get0_data(algor_mgf1_s); 846 | if (ASN1_STRING_length(algor_mgf1_s) == sizeof mgf1_s_data) { 847 | memcpy(mgf1_s_data, alg_data, sizeof mgf1_s_data); 848 | } else if (ASN1_STRING_length(algor_mgf1_s) == sizeof mgf1_s_data + 2) { // Trim NULL 849 | if (alg_data[1] != sizeof mgf1_s_data || alg_data[3] != 9 || 850 | alg_data[sizeof mgf1_s_data] != 5 || alg_data[1 + sizeof mgf1_s_data] != 0) { 851 | OPENSSL_free(spki_bytes); 852 | ASN1_STRING_free(algor_mgf1_s); 853 | return -1; 854 | } 855 | memcpy(mgf1_s_data, alg_data, sizeof mgf1_s_data); 856 | mgf1_s_data[1] -= 2; 857 | } else { 858 | OPENSSL_free(spki_bytes); 859 | ASN1_STRING_free(algor_mgf1_s); 860 | return -1; 861 | } 862 | memcpy(&spki_bytes[21], mgf1_s_data, sizeof mgf1_s_data); 863 | memcpy(&spki_bytes[49], mgf1_s_data, sizeof mgf1_s_data); 864 | ASN1_STRING_free(algor_mgf1_s); 865 | 866 | spki->bytes = spki_bytes; 867 | spki->bytes_len = template_len + spki_raw.bytes_len; 868 | 869 | return 0; 870 | } 871 | 872 | int 873 | brsa_publickey_import_spki(const BRSAContext *context, BRSAPublicKey *pk, const uint8_t *spki, 874 | const size_t spki_len) 875 | { 876 | (void) context; 877 | 878 | const size_t template_len = sizeof rsassa_pss_s_template; 879 | if (spki_len > MAX_SERIALIZED_PK_LEN || spki_len <= template_len) { 880 | return -1; 881 | } 882 | if (memcmp(&rsassa_pss_s_template[6], &spki[6], 18 - 6) != 0) { 883 | return -1; 884 | } 885 | const size_t alg_len = spki[5]; 886 | if (spki_len <= alg_len + 11) { 887 | return -1; 888 | } 889 | return brsa_publickey_import(pk, &spki[alg_len + 11], spki_len - alg_len - 11); 890 | } 891 | 892 | int 893 | brsa_publickey_id(const BRSAContext *context, uint8_t *id, size_t id_len, const BRSAPublicKey *pk) 894 | { 895 | BRSASerializedKey spki; 896 | 897 | if (brsa_publickey_export_spki(context, &spki, pk) != 0) { 898 | return -1; 899 | } 900 | 901 | uint8_t h[SHA256_DIGEST_LENGTH]; 902 | SHA256_CTX hash_ctx; 903 | if (SHA256_Init(&hash_ctx) != ERR_LIB_NONE || 904 | SHA256_Update(&hash_ctx, spki.bytes, spki.bytes_len) != ERR_LIB_NONE || 905 | SHA256_Final(h, &hash_ctx) != ERR_LIB_NONE) { 906 | return -1; 907 | } 908 | 909 | brsa_serializedkey_deinit(&spki); 910 | 911 | size_t out_len = id_len; 912 | if (out_len > sizeof h) { 913 | out_len = sizeof h; 914 | memset(id + out_len, 0, id_len - out_len); 915 | } 916 | memcpy(id, h, out_len); 917 | 918 | return 0; 919 | } 920 | -------------------------------------------------------------------------------- /src/blind_rsa.h: -------------------------------------------------------------------------------- 1 | #ifndef blind_rsa_H 2 | #define blind_rsa_H 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #define BRSA_DEFAULT_SALT_LENGTH ((size_t) -1) 14 | 15 | // Hash functions 16 | typedef enum BRSAHashFunction { 17 | BRSA_SHA256, 18 | BRSA_SHA384, 19 | BRSA_SHA512, 20 | } BRSAHashFunction; 21 | 22 | // Context 23 | typedef struct BRSAContext { 24 | const EVP_MD *evp_md; 25 | size_t salt_len; 26 | } BRSAContext; 27 | 28 | // Blind message to be signed 29 | typedef struct BRSABlindMessage { 30 | uint8_t *blind_message; 31 | size_t blind_message_len; 32 | } BRSABlindMessage; 33 | 34 | // Secret blinding factor 35 | typedef struct BRSABlindingSecret { 36 | uint8_t *secret; 37 | size_t secret_len; 38 | } BRSABlindingSecret; 39 | 40 | // RSA blind signature 41 | typedef struct BRSABlindSignature { 42 | uint8_t *blind_sig; 43 | size_t blind_sig_len; 44 | } BRSABlindSignature; 45 | 46 | // RSA signature of an unblinded message 47 | typedef struct BRSASignature { 48 | uint8_t *sig; 49 | size_t sig_len; 50 | } BRSASignature; 51 | 52 | // An RSA public key 53 | typedef struct BRSAPublicKey { 54 | EVP_PKEY *evp_pkey; 55 | BN_MONT_CTX *mont_ctx; 56 | } BRSAPublicKey; 57 | 58 | // An RSA secret key 59 | typedef struct BRSASecretKey { 60 | EVP_PKEY *evp_pkey; 61 | } BRSASecretKey; 62 | 63 | // A serialized representation of a key 64 | typedef struct BRSASerializedKey { 65 | uint8_t *bytes; 66 | size_t bytes_len; 67 | } BRSASerializedKey; 68 | 69 | // A message randomizer ("noise" added before the message to be signed) 70 | typedef struct BRSAMessageRandomizer { 71 | uint8_t noise[32]; 72 | } BRSAMessageRandomizer; 73 | 74 | // Initialize a standard context for probabilistic padding (recommended for most applications) 75 | void brsa_context_init_default(BRSAContext *context) __attribute__((nonnull)); 76 | 77 | // Initialize a context for deterministic padding 78 | void brsa_context_init_deterministic(BRSAContext *context) __attribute__((nonnull)); 79 | 80 | // Initialize a context with custom parameters. 81 | // The salt length can be set to BRSA_DEFAULT_SALT_LENGTH to match the hash function output size 82 | int brsa_context_init_custom(BRSAContext *context, BRSAHashFunction hash_function, size_t salt_len) 83 | __attribute__((nonnull)); 84 | 85 | // Generate a new key pair, and put the key pair into `sk` and a key with the public information 86 | // only into `pk` 87 | int brsa_keypair_generate(BRSASecretKey *sk, BRSAPublicKey *pk, int modulus_bits) 88 | __attribute__((nonnull(1))); 89 | 90 | // Import a DER-serialized secret key into `sk` 91 | int brsa_secretkey_import(BRSASecretKey *sk, const uint8_t *der, const size_t der_len) 92 | __attribute__((nonnull)); 93 | 94 | // Export a secret key into a DER representation and put the result into `serialized` 95 | int brsa_secretkey_export(BRSASerializedKey *serialized, const BRSASecretKey *sk) 96 | __attribute__((nonnull)); 97 | 98 | // Import a DER-serialized public key into `pk` 99 | int brsa_publickey_import(BRSAPublicKey *pk, const uint8_t *der, const size_t der_len) 100 | __attribute__((nonnull)); 101 | 102 | // Export a public key into a DER representation, and put the result into `serialized` 103 | int brsa_publickey_export(BRSASerializedKey *serialized, const BRSAPublicKey *pk) 104 | __attribute__((nonnull)); 105 | 106 | // Recover a public key from a secret key 107 | int brsa_publickey_recover(BRSAPublicKey *pk, const BRSASecretKey *sk) __attribute__((nonnull)); 108 | 109 | // Put the SubjectPublicKeyInfo for the public key into `spki` 110 | int brsa_publickey_export_spki(const BRSAContext *context, BRSASerializedKey *spki, 111 | const BRSAPublicKey *pk) __attribute__((nonnull)); 112 | 113 | // Import a public key encoded as SPKI. 114 | int brsa_publickey_import_spki(const BRSAContext *context, BRSAPublicKey *pk, const uint8_t *spki, 115 | const size_t spki_len) __attribute__((nonnull)); 116 | 117 | // Return an identifier for a public key. 118 | // Up to `id_len` bytes will be stored into `id`. 119 | int brsa_publickey_id(const BRSAContext *context, uint8_t *id, size_t id_len, 120 | const BRSAPublicKey *pk) __attribute__((nonnull)); 121 | 122 | // Free the internal structures of a secret key 123 | void brsa_secretkey_deinit(BRSASecretKey *sk); 124 | 125 | // Free the internal structures of a public key 126 | void brsa_publickey_deinit(BRSAPublicKey *pk); 127 | 128 | // Free thel internal structures of a serialized key 129 | void brsa_serializedkey_deinit(BRSASerializedKey *serialized); 130 | 131 | // Free the internal structures of a blind message 132 | void brsa_blind_message_deinit(BRSABlindMessage *blind_message); 133 | 134 | // Free the internal structures of a secret blinding factor 135 | void brsa_blinding_secret_deinit(BRSABlindingSecret *secret); 136 | 137 | // Free the internal structures of a blind signature 138 | void brsa_blind_signature_deinit(BRSABlindSignature *blind_sig); 139 | 140 | // Free the internal structures of a signature 141 | void brsa_signature_deinit(BRSASignature *blind_sig); 142 | 143 | // Generate a random message of length `msg_len`, blind it using the public 144 | // key `pk` and put the serialized blind message into `blind_message`, as well as 145 | // the secret blinding factor into `secret` 146 | int brsa_blind_message_generate(const BRSAContext *context, BRSABlindMessage *blind_message, 147 | uint8_t *msg, size_t msg_len, BRSABlindingSecret *secret, 148 | BRSAPublicKey *pk) __attribute__((nonnull)); 149 | 150 | // Blind a message `msg` of length `msg_len` bytes, using the public RSA key 151 | // `pk`, and put the serialized blind message into `blind_message`, as well as 152 | // the secret blinding factor into `secret` 153 | int brsa_blind(const BRSAContext *context, BRSABlindMessage *blind_message, 154 | BRSABlindingSecret *secret, BRSAMessageRandomizer *msg_randomizer, BRSAPublicKey *pk, 155 | const uint8_t *msg, size_t msg_len) __attribute__((nonnull(1, 2, 3, 5, 6))); 156 | 157 | // Compute a signature for a blind message `blind_message` of 158 | // length `blind_message_len` bytes using a key pair `sk`, and put the 159 | // serialized signature into `blind_sig` 160 | int brsa_blind_sign(const BRSAContext *context, BRSABlindSignature *blind_sig, BRSASecretKey *sk, 161 | const BRSABlindMessage *blind_message) __attribute__((nonnull)); 162 | 163 | // Compute a signature for a message `msg` given the signature `blind(msg, secret)`. 164 | // The signature of `msg` is put into `sig`. Note that before returning, the function 165 | // automatically verifies that the new signature is valid for the given public key. 166 | int brsa_finalize(const BRSAContext *context, BRSASignature *sig, 167 | const BRSABlindSignature *blind_sig, const BRSABlindingSecret *secret_, 168 | const BRSAMessageRandomizer *msg_randomizer, BRSAPublicKey *pk, 169 | const uint8_t *msg, size_t msg_len) __attribute__((nonnull(1, 2, 3, 4, 6, 7))); 170 | 171 | // Verify a non-blind signature `sig` for a message `msg` of length `msg_len` using the public key 172 | // `pk`. The function returns `0` if the signature if valid, and `-1` on error. 173 | int brsa_verify(const BRSAContext *context, const BRSASignature *sig, BRSAPublicKey *pk, 174 | const BRSAMessageRandomizer *msg_randomizer, const uint8_t *msg, size_t msg_len) 175 | __attribute__((nonnull(1, 2, 3, 5))) __attribute__((warn_unused_result)); 176 | 177 | #ifdef __cplusplus 178 | } 179 | #endif 180 | #endif 181 | -------------------------------------------------------------------------------- /src/test_blind_rsa.c: -------------------------------------------------------------------------------- 1 | #undef NDEBUG 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "blind_rsa.h" 12 | 13 | static void 14 | test_default(void) 15 | { 16 | // Context 17 | BRSAContext context; 18 | brsa_context_init_default(&context); 19 | 20 | // [SERVER]: Generate a RSA-2048 key pair 21 | BRSASecretKey sk; 22 | BRSAPublicKey pk; 23 | assert(brsa_keypair_generate(&sk, &pk, 2048) == 0); 24 | 25 | // Noise is not required if the message is random. 26 | // If it is not NULL, it will be automatically filled by brsa_blind_sign(). 27 | BRSAMessageRandomizer *msg_randomizer = NULL; 28 | 29 | // [CLIENT]: create a random message and blind it for the server whose public key is `pk`. 30 | // The client must store the message and the secret. 31 | uint8_t msg[32]; 32 | const size_t msg_len = sizeof msg; 33 | BRSABlindMessage blind_msg; 34 | BRSABlindingSecret client_secret; 35 | assert(brsa_blind_message_generate(&context, &blind_msg, msg, msg_len, &client_secret, &pk) == 36 | 0); 37 | 38 | // [SERVER]: compute a signature for a blind message, to be sent to the client. 39 | // The client secret should not be sent to the server. 40 | BRSABlindSignature blind_sig; 41 | assert(brsa_blind_sign(&context, &blind_sig, &sk, &blind_msg) == 0); 42 | brsa_blind_message_deinit(&blind_msg); 43 | 44 | // [CLIENT]: later, when the client wants to redeem a signed blind message, 45 | // using the blinding secret, it can locally compute the signature of the 46 | // original message. 47 | // The client then owns a new valid (message, signature) pair, and the 48 | // server cannot link it to a previous(blinded message, blind signature) pair. 49 | // Note that the finalization function also verifies that the signature is 50 | // correct for the server public key. 51 | BRSASignature sig; 52 | assert(brsa_finalize( 53 | &context, &sig, &blind_sig, &client_secret, msg_randomizer, &pk, msg, msg_len) == 0); 54 | brsa_blind_signature_deinit(&blind_sig); 55 | brsa_blinding_secret_deinit(&client_secret); 56 | 57 | // [SERVER]: a non-blind signature can be verified using the server's public key. 58 | assert(brsa_verify(&context, &sig, &pk, msg_randomizer, msg, msg_len) == 0); 59 | brsa_signature_deinit(&sig); 60 | 61 | // Get a key identifier 62 | uint8_t key_id[4]; 63 | assert(brsa_publickey_id(&context, key_id, sizeof key_id, &pk) == 0); 64 | 65 | // Key serialization 66 | BRSASerializedKey sk_der, pk_der; 67 | assert(brsa_secretkey_export(&sk_der, &sk) == 0); 68 | assert(brsa_publickey_export(&pk_der, &pk) == 0); 69 | 70 | // Free key resources 71 | brsa_secretkey_deinit(&sk); 72 | brsa_publickey_deinit(&pk); 73 | 74 | // Key deserialization 75 | assert(brsa_secretkey_import(&sk, sk_der.bytes, sk_der.bytes_len) == 0); 76 | assert(brsa_publickey_import(&pk, pk_der.bytes, pk_der.bytes_len) == 0); 77 | brsa_serializedkey_deinit(&sk_der); 78 | brsa_serializedkey_deinit(&pk_der); 79 | 80 | brsa_secretkey_deinit(&sk); 81 | brsa_publickey_deinit(&pk); 82 | } 83 | 84 | static void 85 | test_deterministic(void) 86 | { 87 | // Context 88 | BRSAContext context; 89 | brsa_context_init_deterministic(&context); 90 | 91 | // [SERVER]: Generate a RSA-2048 key pair 92 | BRSASecretKey sk; 93 | BRSAPublicKey pk; 94 | assert(brsa_keypair_generate(&sk, &pk, 2048) == 0); 95 | 96 | // Noise is not required if the message is random. 97 | BRSAMessageRandomizer *msg_randomizer = NULL; 98 | 99 | // [CLIENT]: create a random message and blind it for the server whose public key is `pk`. 100 | // The client must store the message and the secret. 101 | uint8_t msg[32]; 102 | const size_t msg_len = sizeof msg; 103 | BRSABlindMessage blind_msg; 104 | BRSABlindingSecret client_secret; 105 | assert(brsa_blind_message_generate(&context, &blind_msg, msg, msg_len, &client_secret, &pk) == 106 | 0); 107 | 108 | // [SERVER]: compute a signature for a blind message, to be sent to the client. 109 | // The client secret should not be sent to the server. 110 | BRSABlindSignature blind_sig; 111 | assert(brsa_blind_sign(&context, &blind_sig, &sk, &blind_msg) == 0); 112 | 113 | // [CLIENT]: later, when the client wants to redeem a signed blind message, 114 | // using the blinding secret, it can locally compute the signature of the 115 | // original message. 116 | // The client then owns a new valid (message, signature) pair, and the 117 | // server cannot link it to a previous(blinded message, blind signature) pair. 118 | // Note that the finalization function also verifies that the signature is 119 | // correct for the server public key. 120 | BRSASignature sig; 121 | assert(brsa_finalize( 122 | &context, &sig, &blind_sig, &client_secret, msg_randomizer, &pk, msg, msg_len) == 0); 123 | brsa_blinding_secret_deinit(&client_secret); 124 | 125 | // [SERVER]: a non-blind signature can be verified using the server's public key. 126 | assert(brsa_verify(&context, &sig, &pk, msg_randomizer, msg, msg_len) == 0); 127 | brsa_signature_deinit(&sig); 128 | 129 | // [CLIENT]: sign the previous message again. 130 | BRSABlindSignature blind_sig2; 131 | assert(brsa_blind_sign(&context, &blind_sig2, &sk, &blind_msg) == 0); 132 | brsa_blind_message_deinit(&blind_msg); 133 | brsa_secretkey_deinit(&sk); 134 | brsa_publickey_deinit(&pk); 135 | 136 | // Check that the blind signature is the same as the previous one. 137 | assert(memcmp(blind_sig.blind_sig, blind_sig2.blind_sig, blind_sig.blind_sig_len) == 0); 138 | 139 | brsa_blind_signature_deinit(&blind_sig); 140 | brsa_blind_signature_deinit(&blind_sig2); 141 | } 142 | 143 | static void 144 | test_custom_parameters(void) 145 | { 146 | BRSAContext context; 147 | brsa_context_init_custom(&context, BRSA_SHA256, 48); 148 | 149 | BRSASecretKey sk; 150 | BRSAPublicKey pk; 151 | assert(brsa_keypair_generate(&sk, &pk, 2048) == 0); 152 | 153 | // Noise is not required if the message is random. 154 | BRSAMessageRandomizer *msg_randomizer = NULL; 155 | 156 | uint8_t msg[32]; 157 | const size_t msg_len = sizeof msg; 158 | BRSABlindMessage blind_msg; 159 | BRSABlindingSecret client_secret; 160 | assert(brsa_blind_message_generate(&context, &blind_msg, msg, msg_len, &client_secret, &pk) == 161 | 0); 162 | 163 | BRSABlindSignature blind_sig; 164 | assert(brsa_blind_sign(&context, &blind_sig, &sk, &blind_msg) == 0); 165 | brsa_blind_message_deinit(&blind_msg); 166 | 167 | BRSASignature sig; 168 | assert(brsa_finalize( 169 | &context, &sig, &blind_sig, &client_secret, msg_randomizer, &pk, msg, msg_len) == 0); 170 | brsa_blind_signature_deinit(&blind_sig); 171 | brsa_blinding_secret_deinit(&client_secret); 172 | 173 | assert(brsa_verify(&context, &sig, &pk, msg_randomizer, msg, msg_len) == 0); 174 | brsa_signature_deinit(&sig); 175 | 176 | brsa_secretkey_deinit(&sk); 177 | brsa_publickey_deinit(&pk); 178 | } 179 | 180 | int 181 | main(void) 182 | { 183 | test_default(); 184 | test_deterministic(); 185 | test_custom_parameters(); 186 | 187 | return 0; 188 | } 189 | --------------------------------------------------------------------------------