├── BUILD ├── CONTRIBUTING ├── LICENSE ├── Makefile ├── README.md ├── base64.c ├── base64.h ├── cbb.c ├── cbs.c ├── example ├── Makefile ├── README.md ├── client.c ├── custom_ext_resume.patch ├── runtest.sh ├── server.c ├── test.crt ├── test.key └── unescape.c ├── tb_bytestring.h ├── token_bind_client.c ├── token_bind_client.h ├── token_bind_common.c ├── token_bind_common.h ├── token_bind_server.c └── token_bind_server.h /BUILD: -------------------------------------------------------------------------------- 1 | licenses(["notice"]) # Apache 2.0 2 | 3 | exports_files(["LICENSE"]) 4 | 5 | cc_library( 6 | name = "token_bind", 7 | srcs = [ 8 | "tb_bytestring.h", 9 | "token_bind_client.c", 10 | "token_bind_common.c", 11 | "token_bind_server.c", 12 | ], 13 | hdrs = [ 14 | "token_bind_client.h", 15 | "token_bind_common.h", 16 | "token_bind_server.h", 17 | ], 18 | includes = [ 19 | ".", 20 | ], 21 | visibility = [ 22 | "//visibility:public", 23 | ], 24 | deps = [ 25 | "@boringssl//:crypto", 26 | "@boringssl//:ssl", 27 | ], 28 | ) 29 | -------------------------------------------------------------------------------- /CONTRIBUTING: -------------------------------------------------------------------------------- 1 | Want to contribute? Great! First, read this page (including the small print at the end). 2 | 3 | ### Before you contribute 4 | Before we can use your code, you must sign the 5 | [Google Individual Contributor License Agreement] 6 | (https://cla.developers.google.com/about/google-individual) 7 | (CLA), which you can do online. The CLA is necessary mainly because you own the 8 | copyright to your changes, even after your contribution becomes part of our 9 | codebase, so we need your permission to use and distribute your code. We also 10 | need to be sure of various other things-for instance that you'll tell us if you 11 | know that your code infringes on other people's patents. You don't have to sign 12 | the CLA until after you've submitted your code for review and a member has 13 | approved it, but you must do it before we can put your code into our codebase. 14 | Before you start working on a larger contribution, you should get in touch with 15 | us first through the issue tracker with your idea so that we can help out and 16 | possibly guide you. Coordinating up front makes it much easier to avoid 17 | frustration later on. 18 | 19 | ### Code reviews 20 | All submissions, including submissions by project members, require review. We 21 | use Github pull requests for this purpose. 22 | 23 | ### The small print 24 | Contributions made by corporations are covered by a different agreement than 25 | the one above, the 26 | [Software Grant and Corporate Contributor License Agreement] 27 | (https://cla.developers.google.com/about/google-corporate). 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Change this to point to your openssl source. 2 | OPENSSL_DIR=openssl 3 | CC=gcc 4 | CFLAGS=-Wall -Wextra -O3 -std=c99 -fpic -pthread -I$(OPENSSL_DIR)/include 5 | 6 | all: token_bind_client.so token_bind_server.so base64.so 7 | 8 | token_bind_client.so: token_bind_client.c token_bind_common.c token_bind_client.h token_bind_common.h tb_bytestring.h cbs.c cbb.c 9 | $(CC) $(CFLAGS) -shared -o token_bind_client.so token_bind_client.c token_bind_common.c cbs.c cbb.c 10 | 11 | token_bind_server.so: token_bind_server.c token_bind_common.c token_bind_server.h token_bind_common.h tb_bytestring.h cbs.c cbb.c 12 | $(CC) $(CFLAGS) -shared -o token_bind_server.so token_bind_server.c token_bind_common.c cbs.c cbb.c 13 | 14 | base64.so: base64.c base64.h 15 | $(CC) $(CFLAGS) -shared -o base64.so base64.c 16 | 17 | clean: 18 | $(RM) -f token_bind_client.so token_bind_server.so base64.so 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | URL: https://github.com/google/token_bind 2 | Version: 0.10 3 | License: Apache 2.0 4 | License File: LICENSE 5 | 6 | Description: 7 | Provides support for token binding according to the following RFCs: 8 | 9 | https://tools.ietf.org/html/draft-ietf-tokbind-protocol-18 10 | https://tools.ietf.org/html/draft-ietf-tokbind-negotiation-10 11 | https://tools.ietf.org/html/draft-ietf-tokbind-https-15 12 | 13 | This token binding library links to OpenSSL to provide token binding negotiation 14 | over TLS, and provides high level functions needed to add token binding support 15 | to HTTP applications. 16 | 17 | This is compatible with OpenSSL versions 1.1.0 and newer. It is implemented 18 | using the custom extension API in OpenSSL. Due to a minor issue 19 | (https://github.com/openssl/openssl/pull/927) in this API, resumption in OpenSSL 1.1.0 20 | is not compatible with this token binding library unless a 1-line patch is made (see 21 | example/custom_ext_resume.patch). This patch should be included in OpenSSL >= 1.1.0. 22 | 23 | This is not an official Google product. 24 | -------------------------------------------------------------------------------- /base64.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2003-2009, 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. */ 14 | 15 | /* This was adapted from Google's Omaha open-source project. */ 16 | 17 | #include "base64.h" 18 | 19 | size_t CalculateBase64EscapedLen(size_t input_len, bool do_padding) { 20 | /* these formulae were copied from comments that used to go with the base64 21 | encoding functions */ 22 | if (((input_len << 4) >> 4) != input_len) { 23 | /* Too large to allocate */ 24 | return 0; 25 | } 26 | size_t intermediate_result = 8 * input_len + 5; 27 | int len = intermediate_result / 6; 28 | if (do_padding) len = ((len + 3) / 4) * 4; 29 | return len; 30 | } 31 | 32 | static size_t Base64EscapeInternal(const char *src, size_t szsrc, 33 | char *dest, size_t szdest, 34 | const char *base64, 35 | bool do_padding) { 36 | if (base64 == NULL || dest == NULL || src == NULL || szsrc <= 0) { 37 | return 0; 38 | } 39 | static const char kPad64 = '='; 40 | char* cur_dest = dest; 41 | const unsigned char *cur_src = (const unsigned char*)src; 42 | 43 | /* Three bytes of data encodes to four characters of cyphertext. 44 | So we can pump through three-byte chunks atomically. */ 45 | while (szsrc > 2) { 46 | /* Keep going until we have less than 24 bits. */ 47 | if (szdest < 4 ) { 48 | return 0; 49 | } 50 | szdest -= 4; 51 | cur_dest[0] = base64[cur_src[0] >> 2]; 52 | cur_dest[1] = base64[((cur_src[0] & 0x03) << 4) + (cur_src[1] >> 4)]; 53 | cur_dest[2] = base64[((cur_src[1] & 0x0f) << 2) + (cur_src[2] >> 6)]; 54 | cur_dest[3] = base64[cur_src[2] & 0x3f]; 55 | cur_dest += 4; 56 | cur_src += 3; 57 | szsrc -= 3; 58 | } 59 | 60 | /* now deal with the tail (<=2 bytes) */ 61 | switch (szsrc) { 62 | case 0: 63 | /* Nothing left; nothing more to do. */ 64 | break; 65 | case 1: 66 | /* One byte left: this encodes to two characters, and (optionally) 67 | two pad characters to round out the four-character cypherblock. */ 68 | if (szdest < 2) { 69 | return 0; 70 | } 71 | szdest -= 2; 72 | cur_dest[0] = base64[cur_src[0] >> 2]; 73 | cur_dest[1] = base64[(cur_src[0] & 0x03) << 4]; 74 | cur_dest += 2; 75 | if (do_padding) { 76 | if (szdest < 2) { 77 | return 0; 78 | } 79 | szdest -= 2; 80 | cur_dest[0] = kPad64; 81 | cur_dest[1] = kPad64; 82 | cur_dest += 2; 83 | } 84 | break; 85 | case 2: 86 | /* Two bytes left: this encodes to three characters, and (optionally) 87 | one pad character to round out the four-character cypherblock. */ 88 | if (szdest < 3) { 89 | return 0; 90 | } 91 | szdest -= 3; 92 | cur_dest[0] = base64[cur_src[0] >> 2]; 93 | cur_dest[1] = base64[((cur_src[0] & 0x03) << 4) + (cur_src[1] >> 4)]; 94 | cur_dest[2] = base64[(cur_src[1] & 0x0f) << 2]; 95 | cur_dest += 3; 96 | if (do_padding) { 97 | if (szdest < 1) { 98 | return 0; 99 | } 100 | szdest -= 1; 101 | cur_dest[0] = kPad64; 102 | cur_dest += 1; 103 | } 104 | break; 105 | default: 106 | /* Should not be reached: blocks of 3 bytes are handled 107 | in the while loop before this switch statement. */ 108 | return 0; 109 | } 110 | return cur_dest - dest; 111 | } 112 | 113 | #define kBase64Chars "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" 114 | 115 | #define kWebSafeBase64Chars "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" 116 | 117 | size_t Base64Escape(const char* src, size_t szsrc, char* dest, size_t szdest, 118 | bool do_padding) { 119 | return Base64EscapeInternal(src, szsrc, dest, szdest, kBase64Chars, do_padding); 120 | } 121 | 122 | size_t WebSafeBase64Escape(const char* src, size_t szsrc, char* dest, 123 | size_t szdest, bool do_padding) { 124 | return Base64EscapeInternal(src, szsrc, dest, szdest, kWebSafeBase64Chars, 125 | do_padding); 126 | } 127 | 128 | /* Check out 129 | http://www.cis.ohio-state.edu/htbin/rfc/rfc2045.html for formal 130 | description, but what we care about is that... 131 | Take the encoded stuff in groups of 4 characters and turn each 132 | character into a code 0 to 63 thus: 133 | A-Z map to 0 to 25 134 | a-z map to 26 to 51 135 | 0-9 map to 52 to 61 136 | +(- for WebSafe) maps to 62 137 | /(_ for WebSafe) maps to 63 138 | There will be four numbers, all less than 64 which can be represented 139 | by a 6 digit binary number (aaaaaa, bbbbbb, cccccc, dddddd respectively). 140 | Arrange the 6 digit binary numbers into three bytes as such: 141 | aaaaaabb bbbbcccc ccdddddd 142 | Equals signs (one or two) are used at the end of the encoded block to 143 | indicate that the text was not an integer multiple of three bytes long. */ 144 | static size_t Base64UnescapeInternal(const char* src, 145 | char* dest, size_t len_dest, 146 | const char* unbase64) { 147 | if (unbase64 == NULL || src == NULL || dest == NULL) { 148 | return 0; 149 | } 150 | static const char kPad64 = '='; 151 | int decode; 152 | size_t destidx = 0; 153 | int state = 0; 154 | /* Used an unsigned char, since ch is used as an array index 155 | (into unbase64). */ 156 | unsigned char ch = 0; 157 | while ((ch = *src++) != '\0') { 158 | if (ch < ' ') { 159 | continue; /* Skip whitespace */ 160 | } 161 | if (ch == kPad64) { 162 | break; 163 | } 164 | decode = unbase64[ch]; 165 | if (decode == 99) { 166 | return 0; /* A non-base64 character */ 167 | } 168 | /* Four cyphertext characters decode to three bytes. Therefore we can be 169 | in one of four states. */ 170 | switch (state) { 171 | case 0: 172 | /* We're at the beginning of a four-character cyphertext block. 173 | This sets the high six bits of the first byte of the plaintext 174 | block. */ 175 | if (destidx >= len_dest) { 176 | return 0; 177 | } 178 | dest[destidx] = (char)(decode << 2); 179 | state = 1; 180 | break; 181 | case 1: 182 | /* We're one character into a four-character cyphertext block. This 183 | sets the low two bits of the first plaintext byte, and the high four 184 | bits of the second plaintext byte. However, if this is the end of 185 | data, and those four bits are zero, it could be that those four bits 186 | are leftovers from the encoding of data that had a length of one mod 187 | three. */ 188 | if (destidx >= len_dest) { 189 | return 0; 190 | } 191 | dest[destidx] |= decode >> 4; 192 | if (destidx + 1 >= len_dest) { 193 | if ((decode & 0x0f) != 0) { 194 | return 0; 195 | } 196 | } else { 197 | dest[destidx + 1] = (char)((decode & 0x0f) << 4); 198 | } 199 | destidx++; 200 | state = 2; 201 | break; 202 | case 2: 203 | /* We're two characters into a four-character cyphertext block. This 204 | sets the low four bits of the second plaintext byte, and the high 205 | two bits of the third plaintext byte. However, if this is the end 206 | of data, and those two bits are zero, it could be that those two 207 | bits are leftovers from the encoding of data that had a length of 208 | two mod three. */ 209 | if (destidx >= len_dest) { 210 | return 0; 211 | } 212 | dest[destidx] |= decode >> 2; 213 | if (destidx +1 >= len_dest) { 214 | if ((decode & 0x03) != 0) { 215 | return (-1); 216 | } 217 | } else { 218 | dest[destidx + 1] = (char)((decode & 0x03) << 6); 219 | } 220 | destidx++; 221 | state = 3; 222 | break; 223 | case 3: 224 | /* We're at the last character of a four-character cyphertext block. 225 | This sets the low six bits of the third plaintext byte. */ 226 | if (destidx >= len_dest) { 227 | return 0; 228 | } 229 | dest[destidx] |= decode; 230 | destidx++; 231 | state = 0; 232 | break; 233 | default: 234 | return 0; 235 | break; 236 | } 237 | } 238 | 239 | /* We are done decoding Base-64 chars. Let's see if we ended on a byte 240 | boundary, and/or with erroneous trailing characters. */ 241 | if (ch == kPad64) { /* We got a pad char */ 242 | if (state == 0 || state == 1) { 243 | return 0; /* Invalid '=' in first or second position */ 244 | } 245 | if (state == 2) { 246 | /* need another '=' */ 247 | while ((ch = *src++) != '\0') { 248 | if (ch < ' ') { 249 | break; 250 | } 251 | } 252 | if (ch != kPad64) { 253 | return 0; 254 | } 255 | } 256 | /* state = 1 or 2, check if all remain padding is space/ */ 257 | while ((ch = *src++) != '\0') { 258 | if (ch > ' ') { 259 | return 0; 260 | } 261 | } 262 | } else { 263 | /* We ended by seeing the end of the string. Make sure we have no partial 264 | bytes lying around. Note that we do not require trailing '=', so states 265 | 2 and 3 are okay too. */ 266 | if (state == 1) 267 | return 0; 268 | } 269 | return destidx; 270 | } 271 | 272 | size_t Base64Unescape(const char* src, char* dest, 273 | size_t len_dest) { 274 | static const char UnBase64[] = { 275 | 99, 99, 99, 99, 99, 99, 99, 99, 276 | 99, 99, 99, 99, 99, 99, 99, 99, 277 | 99, 99, 99, 99, 99, 99, 99, 99, 278 | 99, 99, 99, 99, 99, 99, 99, 99, 279 | 99, 99, 99, 99, 99, 99, 99, 99, 280 | 99, 99, 99, 62/*+*/, 99, 99, 99, 63/*/ */, 281 | 52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/, 282 | 60/*8*/, 61/*9*/, 99, 99, 99, 99, 99, 99, 283 | 99, 0/*A*/, 1/*B*/, 2/*C*/, 3/*D*/, 4/*E*/, 5/*F*/, 6/*G*/, 284 | 7/*H*/, 8/*I*/, 9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/, 285 | 15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/, 286 | 23/*X*/, 24/*Y*/, 25/*Z*/, 99, 99, 99, 99, 99, 287 | 99, 26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/, 288 | 33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/, 289 | 41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/, 290 | 49/*x*/, 50/*y*/, 51/*z*/, 99, 99, 99, 99, 99, 291 | 99, 99, 99, 99, 99, 99, 99, 99, 292 | 99, 99, 99, 99, 99, 99, 99, 99, 293 | 99, 99, 99, 99, 99, 99, 99, 99, 294 | 99, 99, 99, 99, 99, 99, 99, 99, 295 | 99, 99, 99, 99, 99, 99, 99, 99, 296 | 99, 99, 99, 99, 99, 99, 99, 99, 297 | 99, 99, 99, 99, 99, 99, 99, 99, 298 | 99, 99, 99, 99, 99, 99, 99, 99, 299 | 99, 99, 99, 99, 99, 99, 99, 99, 300 | 99, 99, 99, 99, 99, 99, 99, 99, 301 | 99, 99, 99, 99, 99, 99, 99, 99, 302 | 99, 99, 99, 99, 99, 99, 99, 99, 303 | 99, 99, 99, 99, 99, 99, 99, 99, 304 | 99, 99, 99, 99, 99, 99, 99, 99, 305 | 99, 99, 99, 99, 99, 99, 99, 99, 306 | 99, 99, 99, 99, 99, 99, 99, 99 307 | }; 308 | return Base64UnescapeInternal(src, dest, len_dest, UnBase64); 309 | } 310 | 311 | size_t WebSafeBase64Unescape(const char *src, char *dest, size_t szdest) { 312 | static const char UnBase64[] = { 313 | 99, 99, 99, 99, 99, 99, 99, 99, 314 | 99, 99, 99, 99, 99, 99, 99, 99, 315 | 99, 99, 99, 99, 99, 99, 99, 99, 316 | 99, 99, 99, 99, 99, 99, 99, 99, 317 | 99, 99, 99, 99, 99, 99, 99, 99, 318 | 99, 99, 99, 99, 99, 62/*-*/, 99, 99, 319 | 52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/, 320 | 60/*8*/, 61/*9*/, 99, 99, 99, 99, 99, 99, 321 | 99, 0/*A*/, 1/*B*/, 2/*C*/, 3/*D*/, 4/*E*/, 5/*F*/, 6/*G*/, 322 | 7/*H*/, 8/*I*/, 9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/, 323 | 15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/, 324 | 23/*X*/, 24/*Y*/, 25/*Z*/, 99, 99, 99, 99, 63/*_*/, 325 | 99, 26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/, 326 | 33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/, 327 | 41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/, 328 | 49/*x*/, 50/*y*/, 51/*z*/, 99, 99, 99, 99, 99, 329 | 99, 99, 99, 99, 99, 99, 99, 99, 330 | 99, 99, 99, 99, 99, 99, 99, 99, 331 | 99, 99, 99, 99, 99, 99, 99, 99, 332 | 99, 99, 99, 99, 99, 99, 99, 99, 333 | 99, 99, 99, 99, 99, 99, 99, 99, 334 | 99, 99, 99, 99, 99, 99, 99, 99, 335 | 99, 99, 99, 99, 99, 99, 99, 99, 336 | 99, 99, 99, 99, 99, 99, 99, 99, 337 | 99, 99, 99, 99, 99, 99, 99, 99, 338 | 99, 99, 99, 99, 99, 99, 99, 99, 339 | 99, 99, 99, 99, 99, 99, 99, 99, 340 | 99, 99, 99, 99, 99, 99, 99, 99, 341 | 99, 99, 99, 99, 99, 99, 99, 99, 342 | 99, 99, 99, 99, 99, 99, 99, 99, 343 | 99, 99, 99, 99, 99, 99, 99, 99, 344 | 99, 99, 99, 99, 99, 99, 99, 99 345 | }; 346 | return Base64UnescapeInternal(src, dest, szdest, UnBase64); 347 | } 348 | -------------------------------------------------------------------------------- /base64.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2003-2009, 2016 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. */ 14 | 15 | #ifndef TOKEN_BIND_CSRC_BASE64_H_ 16 | #define TOKEN_BIND_CSRC_BASE64_H_ 17 | 18 | #include 19 | #include 20 | 21 | /* Return the length to use for the output buffer given to the base64 escape 22 | routines. Make sure to use the same value for do_padding in both. This 23 | function may return 0 if the input length is within 16X of the maximum value 24 | we can represent with size_t. */ 25 | size_t CalculateBase64EscapedLen(size_t input_len, bool do_padding); 26 | 27 | /* WebSafeBase64Escape and Base64Escape encode |src| to |dest| using base64 28 | encoding. |src| is not null terminated, instead specify len. |dest| should 29 | have at least CalculateBase64EscapedLen() length. Returns the length of 30 | |dest|. The WebSafe variation uses '-' instead of '+' and '_' instead of 31 | '/' so that we can place the output in a URL or cookies without having to 32 | escape them. It also has an extra parameter "do_padding", which when set to 33 | false will prevent padding with "=". */ 34 | size_t WebSafeBase64Escape(const char* src, size_t slen, char* dest, 35 | size_t szdest, bool do_padding); 36 | size_t Base64Escape(const char* src, size_t szsrc, char* dest, size_t szdest, 37 | bool do_padding); 38 | 39 | /* WebBase64Unescape and Base64Unscape copy |src| to |dest|, where src is in 40 | base64 and is written to its ASCII equivalents. |src| is null terminated. 41 | The WebSafe variation use '-' instead of '+' and '_' instead of '/'. The 42 | functions return the number of characters that are decoded in the 43 | destination buffer or 0 in case of a decoding error. */ 44 | size_t WebSafeBase64Unescape(const char* src, char* dest, size_t szdest); 45 | size_t Base64Unescape(const char* src, char* dest, size_t szdest); 46 | 47 | #endif // TOKEN_BIND_CSRC_BASE64_H_ 48 | -------------------------------------------------------------------------------- /cbb.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. */ 14 | 15 | /* This file was copied from BoringSSL and stripped of functions not used by the 16 | * token binding libraries. */ 17 | 18 | #include 19 | #include "tb_bytestring.h" 20 | 21 | #include 22 | #include 23 | 24 | static void tbCBB_zero(tbCBB *cbb) { 25 | memset(cbb, 0, sizeof(tbCBB)); 26 | } 27 | 28 | static int cbb_init(tbCBB *cbb, uint8_t *buf, size_t cap) { 29 | /* This assumes that |cbb| has already been zeroed. */ 30 | struct tb_cbb_buffer_st *base; 31 | 32 | base = malloc(sizeof(struct tb_cbb_buffer_st)); 33 | if (base == NULL) { 34 | return 0; 35 | } 36 | 37 | base->buf = buf; 38 | base->len = 0; 39 | base->cap = cap; 40 | base->can_resize = 1; 41 | base->error = 0; 42 | 43 | cbb->base = base; 44 | cbb->is_top_level = 1; 45 | return 1; 46 | } 47 | 48 | int tbCBB_init(tbCBB *cbb, size_t initial_capacity) { 49 | tbCBB_zero(cbb); 50 | 51 | uint8_t *buf = malloc(initial_capacity); 52 | if (initial_capacity > 0 && buf == NULL) { 53 | return 0; 54 | } 55 | 56 | if (!cbb_init(cbb, buf, initial_capacity)) { 57 | free(buf); 58 | return 0; 59 | } 60 | 61 | return 1; 62 | } 63 | 64 | void tbCBB_cleanup(tbCBB *cbb) { 65 | if (cbb->base) { 66 | /* Only top-level |tbCBB|s are cleaned up. Child |tbCBB|s are non-owning. 67 | * They are implicitly discarded when the parent is flushed or cleaned up. 68 | */ 69 | assert(cbb->is_top_level); 70 | 71 | if (cbb->base->can_resize) { 72 | free(cbb->base->buf); 73 | } 74 | free(cbb->base); 75 | } 76 | cbb->base = NULL; 77 | } 78 | 79 | static int cbb_buffer_reserve(struct tb_cbb_buffer_st *base, uint8_t **out, 80 | size_t len) { 81 | size_t newlen; 82 | 83 | if (base == NULL) { 84 | return 0; 85 | } 86 | 87 | newlen = base->len + len; 88 | if (newlen < base->len) { 89 | /* Overflow */ 90 | goto err; 91 | } 92 | 93 | if (newlen > base->cap) { 94 | size_t newcap = base->cap * 2; 95 | uint8_t *newbuf; 96 | 97 | if (!base->can_resize) { 98 | goto err; 99 | } 100 | 101 | if (newcap < base->cap || newcap < newlen) { 102 | newcap = newlen; 103 | } 104 | newbuf = realloc(base->buf, newcap); 105 | if (newbuf == NULL) { 106 | goto err; 107 | } 108 | 109 | base->buf = newbuf; 110 | base->cap = newcap; 111 | } 112 | 113 | if (out) { 114 | *out = base->buf + base->len; 115 | } 116 | 117 | return 1; 118 | 119 | err: 120 | base->error = 1; 121 | return 0; 122 | } 123 | 124 | static int cbb_buffer_add(struct tb_cbb_buffer_st *base, uint8_t **out, 125 | size_t len) { 126 | if (!cbb_buffer_reserve(base, out, len)) { 127 | return 0; 128 | } 129 | /* This will not overflow or |cbb_buffer_reserve| would have failed. */ 130 | base->len += len; 131 | return 1; 132 | } 133 | 134 | static int cbb_buffer_add_u(struct tb_cbb_buffer_st *base, uint32_t v, 135 | size_t len_len) { 136 | uint8_t *buf; 137 | size_t i; 138 | 139 | if (len_len == 0) { 140 | return 1; 141 | } 142 | if (!cbb_buffer_add(base, &buf, len_len)) { 143 | return 0; 144 | } 145 | 146 | for (i = len_len - 1; i < len_len; i--) { 147 | buf[i] = v; 148 | v >>= 8; 149 | } 150 | 151 | if (v != 0) { 152 | base->error = 1; 153 | return 0; 154 | } 155 | 156 | return 1; 157 | } 158 | 159 | int tbCBB_finish(tbCBB *cbb, uint8_t **out_data, size_t *out_len) { 160 | if (!cbb->is_top_level) { 161 | return 0; 162 | } 163 | 164 | if (!tbCBB_flush(cbb)) { 165 | return 0; 166 | } 167 | 168 | if (cbb->base->can_resize && (out_data == NULL || out_len == NULL)) { 169 | /* |out_data| and |out_len| can only be NULL if the tbCBB is fixed. */ 170 | return 0; 171 | } 172 | 173 | if (out_data != NULL) { 174 | *out_data = cbb->base->buf; 175 | } 176 | if (out_len != NULL) { 177 | *out_len = cbb->base->len; 178 | } 179 | cbb->base->buf = NULL; 180 | tbCBB_cleanup(cbb); 181 | return 1; 182 | } 183 | 184 | /* tbCBB_flush recurses and then writes out any pending length prefix. The 185 | * current length of the underlying base is taken to be the length of the 186 | * length-prefixed data. */ 187 | int tbCBB_flush(tbCBB *cbb) { 188 | size_t child_start, i, len; 189 | 190 | /* If |cbb->base| has hit an error, the buffer is in an undefined state, so 191 | * fail all following calls. In particular, |cbb->child| may point to invalid 192 | * memory. */ 193 | if (cbb->base == NULL || cbb->base->error) { 194 | return 0; 195 | } 196 | 197 | if (cbb->child == NULL || cbb->child->pending_len_len == 0) { 198 | return 1; 199 | } 200 | 201 | child_start = cbb->child->offset + cbb->child->pending_len_len; 202 | 203 | if (!tbCBB_flush(cbb->child) || child_start < cbb->child->offset || 204 | cbb->base->len < child_start) { 205 | goto err; 206 | } 207 | 208 | len = cbb->base->len - child_start; 209 | 210 | if (cbb->child->pending_is_asn1) { 211 | /* For ASN.1 we assume that we'll only need a single byte for the length. 212 | * If that turned out to be incorrect, we have to move the contents along 213 | * in order to make space. */ 214 | uint8_t len_len; 215 | uint8_t initial_length_byte; 216 | 217 | assert(cbb->child->pending_len_len == 1); 218 | 219 | if (len > 0xfffffffe) { 220 | /* Too large. */ 221 | goto err; 222 | } else if (len > 0xffffff) { 223 | len_len = 5; 224 | initial_length_byte = 0x80 | 4; 225 | } else if (len > 0xffff) { 226 | len_len = 4; 227 | initial_length_byte = 0x80 | 3; 228 | } else if (len > 0xff) { 229 | len_len = 3; 230 | initial_length_byte = 0x80 | 2; 231 | } else if (len > 0x7f) { 232 | len_len = 2; 233 | initial_length_byte = 0x80 | 1; 234 | } else { 235 | len_len = 1; 236 | initial_length_byte = (uint8_t)len; 237 | len = 0; 238 | } 239 | 240 | if (len_len != 1) { 241 | /* We need to move the contents along in order to make space. */ 242 | size_t extra_bytes = len_len - 1; 243 | if (!cbb_buffer_add(cbb->base, NULL, extra_bytes)) { 244 | goto err; 245 | } 246 | memmove(cbb->base->buf + child_start + extra_bytes, 247 | cbb->base->buf + child_start, len); 248 | } 249 | cbb->base->buf[cbb->child->offset++] = initial_length_byte; 250 | cbb->child->pending_len_len = len_len - 1; 251 | } 252 | 253 | for (i = cbb->child->pending_len_len - 1; i < cbb->child->pending_len_len; 254 | i--) { 255 | cbb->base->buf[cbb->child->offset + i] = (uint8_t)len; 256 | len >>= 8; 257 | } 258 | if (len != 0) { 259 | goto err; 260 | } 261 | 262 | cbb->child->base = NULL; 263 | cbb->child = NULL; 264 | 265 | return 1; 266 | 267 | err: 268 | cbb->base->error = 1; 269 | return 0; 270 | } 271 | 272 | const uint8_t *tbCBB_data(const tbCBB *cbb) { 273 | assert(cbb->child == NULL); 274 | return cbb->base->buf + cbb->offset + cbb->pending_len_len; 275 | } 276 | 277 | size_t tbCBB_len(const tbCBB *cbb) { 278 | assert(cbb->child == NULL); 279 | assert(cbb->offset + cbb->pending_len_len <= cbb->base->len); 280 | 281 | return cbb->base->len - cbb->offset - cbb->pending_len_len; 282 | } 283 | 284 | static int cbb_add_length_prefixed(tbCBB *cbb, tbCBB *out_contents, 285 | uint8_t len_len) { 286 | uint8_t *prefix_bytes; 287 | 288 | if (!tbCBB_flush(cbb)) { 289 | return 0; 290 | } 291 | 292 | size_t offset = cbb->base->len; 293 | if (!cbb_buffer_add(cbb->base, &prefix_bytes, len_len)) { 294 | return 0; 295 | } 296 | 297 | memset(prefix_bytes, 0, len_len); 298 | memset(out_contents, 0, sizeof(tbCBB)); 299 | out_contents->base = cbb->base; 300 | cbb->child = out_contents; 301 | cbb->child->offset = offset; 302 | cbb->child->pending_len_len = len_len; 303 | cbb->child->pending_is_asn1 = 0; 304 | 305 | return 1; 306 | } 307 | 308 | int tbCBB_add_u8_length_prefixed(tbCBB *cbb, tbCBB *out_contents) { 309 | return cbb_add_length_prefixed(cbb, out_contents, 1); 310 | } 311 | 312 | int tbCBB_add_u16_length_prefixed(tbCBB *cbb, tbCBB *out_contents) { 313 | return cbb_add_length_prefixed(cbb, out_contents, 2); 314 | } 315 | 316 | int tbCBB_add_asn1(tbCBB *cbb, tbCBB *out_contents, unsigned tag) { 317 | if (tag > 0xff || (tag & 0x1f) == 0x1f) { 318 | /* Long form identifier octets are not supported. Further, all current valid 319 | * tag serializations are 8 bits. */ 320 | cbb->base->error = 1; 321 | return 0; 322 | } 323 | 324 | if (!tbCBB_flush(cbb) || 325 | /* |tag|'s representation matches the DER encoding. */ 326 | !tbCBB_add_u8(cbb, (uint8_t)tag)) { 327 | return 0; 328 | } 329 | 330 | size_t offset = cbb->base->len; 331 | if (!tbCBB_add_u8(cbb, 0)) { 332 | return 0; 333 | } 334 | 335 | memset(out_contents, 0, sizeof(tbCBB)); 336 | out_contents->base = cbb->base; 337 | cbb->child = out_contents; 338 | cbb->child->offset = offset; 339 | cbb->child->pending_len_len = 1; 340 | cbb->child->pending_is_asn1 = 1; 341 | 342 | return 1; 343 | } 344 | 345 | int tbCBB_add_bytes(tbCBB *cbb, const uint8_t *data, size_t len) { 346 | uint8_t *dest; 347 | 348 | if (!tbCBB_flush(cbb) || !cbb_buffer_add(cbb->base, &dest, len)) { 349 | return 0; 350 | } 351 | memcpy(dest, data, len); 352 | return 1; 353 | } 354 | 355 | int tbCBB_add_u8(tbCBB *cbb, uint8_t value) { 356 | if (!tbCBB_flush(cbb)) { 357 | return 0; 358 | } 359 | 360 | return cbb_buffer_add_u(cbb->base, value, 1); 361 | } 362 | 363 | int tbCBB_add_u16(tbCBB *cbb, uint16_t value) { 364 | if (!tbCBB_flush(cbb)) { 365 | return 0; 366 | } 367 | 368 | return cbb_buffer_add_u(cbb->base, value, 2); 369 | } 370 | -------------------------------------------------------------------------------- /cbs.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. */ 14 | 15 | /* This file was copied from BoringSSL and stripped of functions not used by the 16 | * token binding libraries. */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include "tb_bytestring.h" 22 | 23 | void tbCBS_init(tbCBS *cbs, const uint8_t *data, size_t len) { 24 | cbs->data = data; 25 | cbs->len = len; 26 | } 27 | 28 | static int cbs_get(tbCBS *cbs, const uint8_t **p, size_t n) { 29 | if (cbs->len < n) { 30 | return 0; 31 | } 32 | 33 | *p = cbs->data; 34 | cbs->data += n; 35 | cbs->len -= n; 36 | return 1; 37 | } 38 | 39 | static int tbCBS_skip(tbCBS *cbs, size_t len) { 40 | const uint8_t *dummy; 41 | return cbs_get(cbs, &dummy, len); 42 | } 43 | 44 | const uint8_t *tbCBS_data(const tbCBS *cbs) { return cbs->data; } 45 | 46 | size_t tbCBS_len(const tbCBS *cbs) { return cbs->len; } 47 | 48 | static int cbs_get_u(tbCBS *cbs, uint32_t *out, size_t len) { 49 | uint32_t result = 0; 50 | size_t i; 51 | const uint8_t *data; 52 | 53 | if (!cbs_get(cbs, &data, len)) { 54 | return 0; 55 | } 56 | for (i = 0; i < len; i++) { 57 | result <<= 8; 58 | result |= data[i]; 59 | } 60 | *out = result; 61 | return 1; 62 | } 63 | 64 | int tbCBS_get_u8(tbCBS *cbs, uint8_t *out) { 65 | const uint8_t *v; 66 | if (!cbs_get(cbs, &v, 1)) { 67 | return 0; 68 | } 69 | *out = *v; 70 | return 1; 71 | } 72 | 73 | int tbCBS_get_u16(tbCBS *cbs, uint16_t *out) { 74 | uint32_t v; 75 | if (!cbs_get_u(cbs, &v, 2)) { 76 | return 0; 77 | } 78 | *out = v; 79 | return 1; 80 | } 81 | 82 | int tbCBS_get_bytes(tbCBS *cbs, tbCBS *out, size_t len) { 83 | const uint8_t *v; 84 | if (!cbs_get(cbs, &v, len)) { 85 | return 0; 86 | } 87 | tbCBS_init(out, v, len); 88 | return 1; 89 | } 90 | 91 | static int cbs_get_length_prefixed(tbCBS *cbs, tbCBS *out, size_t len_len) { 92 | uint32_t len; 93 | if (!cbs_get_u(cbs, &len, len_len)) { 94 | return 0; 95 | } 96 | return tbCBS_get_bytes(cbs, out, len); 97 | } 98 | 99 | int tbCBS_get_u8_length_prefixed(tbCBS *cbs, tbCBS *out) { 100 | return cbs_get_length_prefixed(cbs, out, 1); 101 | } 102 | 103 | int tbCBS_get_u16_length_prefixed(tbCBS *cbs, tbCBS *out) { 104 | return cbs_get_length_prefixed(cbs, out, 2); 105 | } 106 | 107 | static int cbs_get_any_asn1_element(tbCBS *cbs, tbCBS *out, unsigned *out_tag, 108 | size_t *out_header_len, int ber_ok) { 109 | uint8_t tag, length_byte; 110 | tbCBS header = *cbs; 111 | tbCBS throwaway; 112 | 113 | if (out == NULL) { 114 | out = &throwaway; 115 | } 116 | 117 | if (!tbCBS_get_u8(&header, &tag) || !tbCBS_get_u8(&header, &length_byte)) { 118 | return 0; 119 | } 120 | 121 | /* ITU-T X.690 section 8.1.2.3 specifies the format for identifiers with a tag 122 | * number no greater than 30. 123 | * 124 | * If the number portion is 31 (0x1f, the largest value that fits in the 125 | * allotted bits), then the tag is more than one byte long and the 126 | * continuation bytes contain the tag number. This parser only supports tag 127 | * numbers less than 31 (and thus single-byte tags). */ 128 | if ((tag & 0x1f) == 0x1f) { 129 | return 0; 130 | } 131 | 132 | if (out_tag != NULL) { 133 | *out_tag = tag; 134 | } 135 | 136 | size_t len; 137 | /* The format for the length encoding is specified in ITU-T X.690 section 138 | * 8.1.3. */ 139 | if ((length_byte & 0x80) == 0) { 140 | /* Short form length. */ 141 | len = ((size_t)length_byte) + 2; 142 | if (out_header_len != NULL) { 143 | *out_header_len = 2; 144 | } 145 | } else { 146 | /* The high bit indicate that this is the long form, while the next 7 bits 147 | * encode the number of subsequent octets used to encode the length (ITU-T 148 | * X.690 clause 8.1.3.5.b). */ 149 | const size_t num_bytes = length_byte & 0x7f; 150 | uint32_t len32; 151 | 152 | if (ber_ok && (tag & tbCBS_ASN1_CONSTRUCTED) != 0 && num_bytes == 0) { 153 | /* indefinite length */ 154 | if (out_header_len != NULL) { 155 | *out_header_len = 2; 156 | } 157 | return tbCBS_get_bytes(cbs, out, 2); 158 | } 159 | 160 | /* ITU-T X.690 clause 8.1.3.5.c specifies that the value 0xff shall not be 161 | * used as the first byte of the length. If this parser encounters that 162 | * value, num_bytes will be parsed as 127, which will fail the check below. 163 | */ 164 | if (num_bytes == 0 || num_bytes > 4) { 165 | return 0; 166 | } 167 | if (!cbs_get_u(&header, &len32, num_bytes)) { 168 | return 0; 169 | } 170 | /* ITU-T X.690 section 10.1 (DER length forms) requires encoding the length 171 | * with the minimum number of octets. */ 172 | if (len32 < 128) { 173 | /* Length should have used short-form encoding. */ 174 | return 0; 175 | } 176 | if ((len32 >> ((num_bytes - 1) * 8)) == 0) { 177 | /* Length should have been at least one byte shorter. */ 178 | return 0; 179 | } 180 | len = len32; 181 | if (len + 2 + num_bytes < len) { 182 | /* Overflow. */ 183 | return 0; 184 | } 185 | len += 2 + num_bytes; 186 | if (out_header_len != NULL) { 187 | *out_header_len = 2 + num_bytes; 188 | } 189 | } 190 | 191 | return tbCBS_get_bytes(cbs, out, len); 192 | } 193 | 194 | int tbCBS_get_any_asn1_element(tbCBS *cbs, tbCBS *out, unsigned *out_tag, 195 | size_t *out_header_len) { 196 | return cbs_get_any_asn1_element(cbs, out, out_tag, out_header_len, 197 | 0 /* DER only */); 198 | } 199 | 200 | static int cbs_get_asn1(tbCBS *cbs, tbCBS *out, unsigned tag_value, 201 | int skip_header) { 202 | size_t header_len; 203 | unsigned tag; 204 | tbCBS throwaway; 205 | 206 | if (out == NULL) { 207 | out = &throwaway; 208 | } 209 | 210 | if (!tbCBS_get_any_asn1_element(cbs, out, &tag, &header_len) || 211 | tag != tag_value) { 212 | return 0; 213 | } 214 | 215 | if (skip_header && !tbCBS_skip(out, header_len)) { 216 | assert(0); 217 | return 0; 218 | } 219 | 220 | return 1; 221 | } 222 | 223 | int tbCBS_get_asn1(tbCBS *cbs, tbCBS *out, unsigned tag_value) { 224 | return cbs_get_asn1(cbs, out, tag_value, 1 /* skip header */); 225 | } 226 | 227 | int tbCBS_get_asn1_element(tbCBS *cbs, tbCBS *out, unsigned tag_value) { 228 | return cbs_get_asn1(cbs, out, tag_value, 0 /* include header */); 229 | } 230 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | # Change this to point to your openssl source. 2 | OPENSSL_DIR=../openssl 3 | 4 | CC=gcc 5 | CFLAGS=-D_GNU_SOURCE -Wall -Wextra -O3 -std=c99 -fpic -pthread -I$(OPENSSL_DIR)/include -I.. 6 | #CFLAGS=-D_GNU_SOURCE -Wall -Wextra -g -std=c99 -fpic -pthread -I$(OPENSSL_DIR)/include -I.. 7 | LIBS= $(OPENSSL_DIR)/libssl.a $(OPENSSL_DIR)/libcrypto.a -ldl 8 | 9 | all: client server unescape 10 | 11 | client: client.c 12 | $(CC) $(CFLAGS) -o client client.c ../token_bind_client.so ../base64.so $(LIBS) 13 | 14 | server: server.c 15 | $(CC) $(CFLAGS) -o server server.c ../token_bind_server.so ../base64.so $(LIBS) 16 | 17 | unescape: unescape.c 18 | $(CC) $(CFLAGS) -o unescape unescape.c ../token_bind_server.so ../base64.so $(LIBS) 19 | 20 | clean: 21 | rm -f client server key_vault cookie_jar unescape 22 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | To compile the example server against OpenSSL, use version 1.1.0 or newer. If 2 | you use resumption, a 1-line patch needs to be applied to the OpenSSL source so 3 | that custom extensions can be negotiated on resume: 4 | 5 | openssl$ git apply /custom_ext_resume.patch 6 | 7 | Then rebuild openssl. The Makefile targets assume you have created a symbolic 8 | link to the openssl source directory in the token_bind directory: 9 | 10 | token_bind$ ln -s /openssl . 11 | 12 | Then to build: 13 | 14 | token_bind$ make 15 | token_bind$ cd example 16 | example$ make 17 | 18 | To run the example server: 19 | 20 | example$ ./server 21 | 22 | You should be able to connect to it by running a recent version of Chrome and 23 | connect to this server at localhost:40000. It will say "Token binding not 24 | negotiated" if Chrome is not configured to speak token binding. Check 25 | about:flags, and make sure you enable the token binding flag. 26 | 27 | You can run the "runtest.sh" test to see if everything is working. 28 | -------------------------------------------------------------------------------- /example/client.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | Author: waywardgeek@gmail.com (Bill Cox) 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 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | 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 and 14 | limitations under the License. */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | /* Buffer size for reading/writing requests/responses. */ 30 | const size_t kBufferSize = 16384; /* Matches SSL's buffer size */ 31 | 32 | typedef enum { 33 | HTTP_POST, 34 | HTTP_GET, 35 | HTTP_UNKNOWN, 36 | } HttpRequestType; 37 | 38 | struct Connection_st; 39 | struct Key_st; 40 | struct Oracle_st; 41 | struct Cookie_st; 42 | struct CookieJar_st; 43 | typedef struct Connection_st Connection; 44 | typedef struct Key_st Key; 45 | typedef struct Oracle_st Oracle; 46 | typedef struct Cookie_st Cookie; 47 | typedef struct CookieJar_st CookieJar; 48 | 49 | struct Connection_st { 50 | SSL_CTX* ctx; 51 | SSL* ssl; 52 | int port; 53 | int server_fd; 54 | tbKeyType key_type; 55 | }; 56 | 57 | struct Key_st { 58 | char* etld_plus1; 59 | char* encoded_key; 60 | Key* next_key; 61 | tbKeyType key_type; 62 | }; 63 | 64 | struct Oracle_st { 65 | Key* first_key; 66 | }; 67 | 68 | struct Cookie_st { 69 | char* hostname; 70 | char* name; 71 | char* value; 72 | Cookie* next_cookie; 73 | }; 74 | 75 | struct CookieJar_st { 76 | Cookie* first_cookie; 77 | }; 78 | 79 | void usage(void) { 80 | printf( 81 | "Usage: client get url\n" 82 | " e.g. client get https://localhost:40000/user\n" 83 | " client post url variables\n" 84 | " e.g. client post https://localhost:40000/login " 85 | "user=john&pw=password\n" 86 | "Cookies keys are written to a file called cookies\n" 87 | "Public/private keys are written to a file called key_vault\n"); 88 | exit(1); 89 | } 90 | 91 | HttpRequestType parseType(char* type_name) { 92 | if (!strcasecmp(type_name, "get")) { 93 | return HTTP_GET; 94 | } 95 | if (!strcasecmp(type_name, "post")) { 96 | return HTTP_POST; 97 | } 98 | return HTTP_UNKNOWN; 99 | } 100 | 101 | void* checkedCalloc(size_t num, size_t size) { 102 | void* p = calloc(num, size); 103 | if (p == NULL) { 104 | printf("Out of memory\n"); 105 | exit(1); 106 | } 107 | return p; 108 | } 109 | 110 | char* copystring(char* source) { 111 | size_t len = strlen(source); 112 | char* dest = checkedCalloc(len + 1, sizeof(char)); 113 | strcpy(dest, source); 114 | return dest; 115 | } 116 | 117 | /* Parse the host, path, and port number from the url. */ 118 | void parseHostPathAndPort(char* url, char** out_hostname, char** out_path, 119 | int* out_port) { 120 | char* hostname; 121 | char* path; 122 | int port = 443; 123 | char* prefix = "https://"; 124 | int prefix_len = strlen(prefix); 125 | if (strncasecmp(url, prefix, prefix_len)) { 126 | printf("The url parameter must start with http://\n"); 127 | usage(); 128 | } 129 | char* p = url + prefix_len; 130 | char* hostname_start = p; 131 | p = strpbrk(p, ":/"); 132 | if (p == NULL) { 133 | hostname = copystring(hostname_start); 134 | path = copystring("/"); 135 | } else { 136 | int hostname_len = p - hostname_start; 137 | hostname = checkedCalloc(hostname_len + 1, sizeof(char)); 138 | memcpy(hostname, hostname_start, hostname_len); 139 | hostname[hostname_len] = '\0'; 140 | if (*p == ':') { 141 | p++; 142 | char* endptr; 143 | port = strtol(p, &endptr, 10); 144 | if (endptr == p) { 145 | printf("Invalid port number\n"); 146 | usage(); 147 | } 148 | p = endptr; 149 | } 150 | if (*p == '\0') { 151 | path = copystring("/"); 152 | } else { 153 | if (*p != '/') { 154 | printf("Expecting / in url\n"); 155 | usage(); 156 | } 157 | path = copystring(p); 158 | } 159 | } 160 | printf("hostname = %s, path = %s, port=%u\n", hostname, path, port); 161 | *out_hostname = hostname; 162 | *out_path = path; 163 | *out_port = port; 164 | } 165 | 166 | void negotiateTLS(Connection* connection) { 167 | OPENSSL_init_ssl( 168 | OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); 169 | SSL_CTX* ctx = SSL_CTX_new(TLS_client_method()); 170 | connection->ctx = ctx; 171 | if (!tbTLSLibInit()) { 172 | printf("Failed to initialize TLS token binding negotiation library\n"); 173 | exit(1); 174 | } 175 | if (!tbEnableTLSTokenBindingNegotiation(ctx)) { 176 | printf("Failed to enable TLS token binding negotiation\n"); 177 | exit(1); 178 | } 179 | connection->ssl = SSL_new(connection->ctx); 180 | SSL_set_fd(connection->ssl, connection->server_fd); 181 | SSL_connect(connection->ssl); 182 | tbKeyType key_type = TB_INVALID_KEY_TYPE; 183 | if (!tbTokenBindingEnabled(connection->ssl, &key_type)) { 184 | printf("Connection failed to negotiate token binding\n"); 185 | } else { 186 | printf("Connection negotiated token binding with key type %s\n", 187 | tbGetKeyTypeName(key_type)); 188 | } 189 | connection->key_type = key_type; 190 | } 191 | 192 | Connection* openConnection(char* hostname, int port) { 193 | Connection* connection = checkedCalloc(1, sizeof(Connection)); 194 | connection->server_fd = socket(AF_INET, SOCK_STREAM, 0); 195 | if (connection->server_fd < 0) { 196 | printf("Could not create socket\n"); 197 | exit(1); 198 | } 199 | struct hostent* server = gethostbyname(hostname); 200 | if (server == NULL) { 201 | printf("No such host: %s\n", hostname); 202 | exit(1); 203 | } 204 | struct sockaddr_in addr; 205 | memset(&addr, '\0', sizeof(addr)); 206 | addr.sin_family = AF_INET; 207 | memcpy(server->h_addr, &addr.sin_addr.s_addr, server->h_length); 208 | addr.sin_port = htons(port); 209 | if (connect(connection->server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 210 | 0) { 211 | printf("Could not connect to %s:%u\n", hostname, port); 212 | exit(1); 213 | } 214 | negotiateTLS(connection); 215 | return connection; 216 | } 217 | 218 | /* Get the "effective top-level domain + 1", or ETLD+1. This is harder than it 219 | sounds, because there is a list of exceptions to the rule that this is just 220 | .com (or .edu, etc). For example, google.com.uk is an eTLD+1, 221 | which is why we say "effective". The Chromium source has a good and well 222 | maintained list of exceptions. For this example, we just assume there are 223 | no exceptions. */ 224 | char* getETLDPlus1(char* hostname) { 225 | char* p = strrchr(hostname, '.'); 226 | if (p == NULL) { 227 | /* Hostname has no ".", so just return the whole hostname. */ 228 | return hostname; 229 | } 230 | p = strrchr(p, '.'); 231 | if (p == NULL) { 232 | /* Hostname has only one ".", so just return the whole hostname. */ 233 | return hostname; 234 | } 235 | return p + 1; 236 | } 237 | 238 | Key* findKey(Oracle* oracle, char* etld_plus1) { 239 | Key* key; 240 | for (key = oracle->first_key; 241 | key != NULL && strcasecmp(key->etld_plus1, etld_plus1); 242 | key = key->next_key) 243 | ; 244 | return key; 245 | } 246 | 247 | char* encodeKey(EVP_PKEY* pkey) { 248 | /* Get the length first. */ 249 | size_t length = i2d_PrivateKey(pkey, NULL); 250 | if (length <= 0) { 251 | printf("Unable to convert pkey to text\n"); 252 | exit(1); 253 | } 254 | uint8_t* buf = checkedCalloc(length, sizeof(uint8_t)); 255 | uint8_t* p = buf; 256 | i2d_PrivateKey(pkey, &p); 257 | size_t encoded_len = CalculateBase64EscapedLen(length, false); 258 | char* out = checkedCalloc(encoded_len, sizeof(char)); 259 | WebSafeBase64Escape((char*)buf, length, out, encoded_len, false); 260 | free(buf); 261 | return out; 262 | } 263 | 264 | EVP_PKEY* decodeKey(char* encoded_key, tbKeyType key_type) { 265 | uint8_t key_len = strlen(encoded_key) + 1; 266 | uint8_t* key = checkedCalloc(key_len, sizeof(char)); 267 | key_len = WebSafeBase64Unescape(encoded_key, (char*)key, key_len); 268 | int type; 269 | if (key_type == TB_ECDSAP256) { 270 | type = EVP_PKEY_EC; 271 | } else { 272 | type = EVP_PKEY_RSA; 273 | } 274 | const uint8_t* p = key; 275 | EVP_PKEY* pkey = d2i_PrivateKey(type, NULL, &p, key_len); 276 | free(key); 277 | return pkey; 278 | } 279 | 280 | EVP_PKEY* generateRSA2048Key() { 281 | EVP_PKEY* pkey = NULL; 282 | EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); 283 | if (!ctx || EVP_PKEY_keygen_init(ctx) <= 0 || 284 | EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048) <= 0 || 285 | EVP_PKEY_keygen(ctx, &pkey) <= 0) { 286 | printf("Failed to generate RSA key\n"); 287 | exit(1); 288 | } 289 | return pkey; 290 | } 291 | 292 | EVP_PKEY* generateECDSAP256Key() { 293 | EC_KEY* eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); 294 | if (eckey == NULL || !EC_KEY_generate_key(eckey)) { 295 | printf("Could not generate EC key\n"); 296 | exit(1); 297 | } 298 | EVP_PKEY* pkey = EVP_PKEY_new(); 299 | if (pkey == NULL || !EVP_PKEY_assign_EC_KEY(pkey, eckey)) { 300 | printf("Could not create EVP_PKEY\n"); 301 | exit(1); 302 | } 303 | return pkey; 304 | } 305 | 306 | Key* createKey(Oracle* oracle, char* etld_plus1, tbKeyType key_type, 307 | char* encoded_key) { 308 | Key* key = checkedCalloc(1, sizeof(Key)); 309 | key->key_type = key_type; 310 | key->etld_plus1 = copystring(etld_plus1); 311 | key->encoded_key = copystring(encoded_key); 312 | key->next_key = oracle->first_key; 313 | oracle->first_key = key; 314 | return key; 315 | } 316 | 317 | Key* generateKey(Oracle* oracle, char* etld_plus1, tbKeyType key_type) { 318 | EVP_PKEY* pkey; 319 | switch (key_type) { 320 | case TB_RSA2048_PKCS15: 321 | case TB_RSA2048_PSS: 322 | pkey = generateRSA2048Key(); 323 | break; 324 | case TB_ECDSAP256: 325 | pkey = generateECDSAP256Key(); 326 | break; 327 | default: 328 | printf("Unknown key type\n"); 329 | exit(1); 330 | } 331 | char* encoded_key; 332 | encoded_key = encodeKey(pkey); 333 | EVP_PKEY_free(pkey); 334 | Key* key = createKey(oracle, etld_plus1, key_type, encoded_key); 335 | free(encoded_key); 336 | return key; 337 | } 338 | 339 | void getKeyTokenBindingID(Key* key, uint8_t** out_tokbind_id, 340 | size_t* out_tokbind_id_len) { 341 | EVP_PKEY* pkey = decodeKey(key->encoded_key, key->key_type); 342 | size_t len = i2d_PublicKey(pkey, NULL); 343 | if (len <= 0) { 344 | printf("Unable to convert pkey to text\n"); 345 | exit(1); 346 | } 347 | uint8_t* buf = checkedCalloc(len, sizeof(uint8_t)); 348 | uint8_t* p = buf; 349 | i2d_PublicKey(pkey, &p); 350 | if (!tbConvertDerKeyToTokenBindingID(buf, len, key->key_type, out_tokbind_id, 351 | out_tokbind_id_len)) { 352 | printf("Unable to convert OpenSSL encoded public key to TokenBindingID\n"); 353 | exit(1); 354 | } 355 | free(buf); 356 | EVP_PKEY_free(pkey); 357 | } 358 | 359 | /* Delete the key. */ 360 | static void deleteKey(Oracle* oracle, char* etld_plus1) { 361 | Key* prev_key = NULL; 362 | Key* key; 363 | for (key = oracle->first_key; 364 | key != NULL && strcasecmp(key->etld_plus1, etld_plus1); 365 | key = key->next_key) { 366 | prev_key = key; 367 | } 368 | if (key == NULL) { 369 | printf("Key not found\n"); 370 | exit(1); 371 | } 372 | if (prev_key == NULL) { 373 | oracle->first_key = key->next_key; 374 | } else { 375 | prev_key->next_key = key->next_key; 376 | } 377 | free(key->etld_plus1); 378 | free(key->encoded_key); 379 | free(key); 380 | } 381 | 382 | /* Find or create a token binding key pair compatible with the negotiated key 383 | type. Then return the TokenBindingID for that key. */ 384 | void getTokenBindingID(Connection* connection, Oracle* oracle, char* etld_plus1, 385 | uint8_t** out_tokbind_id, size_t* out_tokbind_id_len) { 386 | Key* key = findKey(oracle, etld_plus1); 387 | if (key != NULL) { 388 | if (connection->key_type == key->key_type) { 389 | getKeyTokenBindingID(key, out_tokbind_id, out_tokbind_id_len); 390 | return; 391 | } 392 | /* The server changed key type, so delete the old token binding key and 393 | create a new one. */ 394 | deleteKey(oracle, etld_plus1); 395 | } 396 | /* We have to create a key pair. */ 397 | key = generateKey(oracle, etld_plus1, connection->key_type); 398 | getKeyTokenBindingID(key, out_tokbind_id, out_tokbind_id_len); 399 | } 400 | 401 | void signMessage(Oracle* oracle, char* etld_plus1, uint8_t* message, 402 | size_t message_len, uint8_t** out_sig, size_t* out_sig_len) { 403 | Key* key = findKey(oracle, etld_plus1); 404 | if (key == NULL) { 405 | printf("Key not found\n"); 406 | exit(1); 407 | } 408 | EVP_PKEY* pkey = decodeKey(key->encoded_key, key->key_type); 409 | EVP_PKEY_CTX* key_ctx; 410 | const size_t kMaxSigLen = 3000; // Big enough for RSA-2048 411 | size_t sig_len = kMaxSigLen; 412 | uint8_t buf[kMaxSigLen]; 413 | EVP_MD_CTX* md_ctx = EVP_MD_CTX_new(); 414 | if (!EVP_DigestSignInit(md_ctx, &key_ctx, EVP_sha256(), NULL, pkey) || 415 | !tbSetPadding(key->key_type, key_ctx) || 416 | !EVP_DigestSignUpdate(md_ctx, message, message_len) || 417 | !EVP_DigestSignFinal(md_ctx, buf, &sig_len) || 418 | !tbConvertDerSigToTokenBindingSig(buf, sig_len, key->key_type, out_sig, 419 | out_sig_len)) { 420 | printf("Signing operation failed\n"); 421 | exit(1); 422 | } 423 | EVP_PKEY_free(pkey); 424 | EVP_MD_CTX_free(md_ctx); 425 | } 426 | 427 | char* generateTokenBindingHeader(Connection* connection, Oracle* oracle, 428 | char* etld_plus1, char* referred_etld_plus1) { 429 | uint8_t* tokbind_id; 430 | size_t tokbind_id_len; 431 | getTokenBindingID(connection, oracle, etld_plus1, &tokbind_id, 432 | &tokbind_id_len); 433 | uint8_t* referred_tokbind_id = NULL; 434 | size_t referred_tokbind_id_len = 0; 435 | if (referred_etld_plus1 != NULL) { 436 | getTokenBindingID(connection, oracle, referred_etld_plus1, 437 | &referred_tokbind_id, &referred_tokbind_id_len); 438 | } 439 | uint8_t ekm[TB_HASH_LEN]; 440 | if (!tbGetEKM(connection->ssl, ekm)) { 441 | printf("Unable to get EKM from TLS connection\n"); 442 | exit(1); 443 | } 444 | uint8_t* data; 445 | size_t data_len; 446 | tbGetDataToSign(ekm, tbGetKeyType(tokbind_id, tokbind_id_len), false, &data, 447 | &data_len); 448 | uint8_t* signature; 449 | size_t signature_len; 450 | signMessage(oracle, etld_plus1, data, data_len, &signature, &signature_len); 451 | free(data); 452 | uint8_t* message; 453 | size_t message_len; 454 | if (!tbBuildTokenBindingMessage(tokbind_id, tokbind_id_len, signature, 455 | signature_len, &message, &message_len)) { 456 | printf("Failed to build token binding message\n"); 457 | exit(1); 458 | } 459 | free(tokbind_id); 460 | if (referred_tokbind_id != NULL) { 461 | free(referred_tokbind_id); 462 | } 463 | free(signature); 464 | size_t buf_len = CalculateBase64EscapedLen(message_len, false); 465 | char* buf = checkedCalloc(buf_len, sizeof(char)); 466 | size_t len = 467 | WebSafeBase64Escape((char*)message, message_len, buf, buf_len, false); 468 | free(message); 469 | char* prefix = "Sec-Token-Binding: "; 470 | size_t tbheader_len = strlen(prefix) + len + 1; 471 | char* tbheader = checkedCalloc(tbheader_len, sizeof(char)); 472 | strcpy(tbheader, prefix); 473 | strcat(tbheader, buf); 474 | return tbheader; 475 | } 476 | 477 | /* This function assumes the entire request is sent in one packet flight, and 478 | not split across SSL_read calls. Also, the maximum request size is limited 479 | to kBufferSize. */ 480 | char* readResponse(Connection* connection) { 481 | char buffer[kBufferSize]; 482 | int num_bytes = SSL_read(connection->ssl, buffer, kBufferSize); 483 | if (num_bytes <= 0) { 484 | printf("Could not read with return val %u\n", num_bytes); 485 | return NULL; 486 | } 487 | return copystring(buffer); 488 | } 489 | 490 | char* sendGetRequest(Connection* connection, char* hostname, int port, 491 | char* path, char* tbheader, char* cookies) { 492 | char request[kBufferSize]; 493 | size_t request_len = snprintf(request, kBufferSize, 494 | "GET %s HTTP/1.0\r\n" 495 | "Host: %s:%u\r\n" 496 | "From: tokbind_test@example.com\r\n" 497 | "User-Agent: token_bind/example/client\r\n" 498 | "%s\r\n" 499 | "%s\r\n", 500 | path, hostname, port, tbheader, cookies); 501 | printf("Sending:\n%s\n", request); 502 | if (SSL_write(connection->ssl, request, request_len) <= 0) { 503 | printf("Unable to write to SSL connection\n"); 504 | exit(1); 505 | } 506 | return readResponse(connection); 507 | } 508 | 509 | char* sendPostRequest(Connection* connection, char* hostname, int port, 510 | char* path, char* tbheader, char* variables, 511 | char* cookies) { 512 | char request[kBufferSize]; 513 | size_t request_len = 514 | snprintf(request, kBufferSize, 515 | "POST %s HTTP/1.0\r\n" 516 | "Host: %s:%u\r\n" 517 | "From: tokbind_test@example.com\r\n" 518 | "User-Agent: token_bind/example/client\r\n" 519 | "%s\r\n" 520 | "%s\r\n" 521 | "%s\r\n", 522 | path, hostname, port, tbheader, cookies, variables); 523 | printf("Sending:\n%s\n", request); 524 | if (SSL_write(connection->ssl, request, request_len) <= 0) { 525 | printf("Unable to write to SSL connection\n"); 526 | exit(1); 527 | } 528 | return readResponse(connection); 529 | } 530 | 531 | void closeConnection(Connection* connection) { 532 | printf("closing connection\n"); 533 | SSL_shutdown(connection->ssl); 534 | SSL_free(connection->ssl); 535 | close(connection->server_fd); 536 | free(connection); 537 | } 538 | 539 | bool readKey(FILE* file, char* etld_plus1, char* key_type_name, 540 | char* encoded_key) { 541 | /* Note: fscanf is unsafe and can cause buffer overflow. It is used for 542 | simplicity in this demo. */ 543 | if (fscanf(file, "%s %s %s\n", etld_plus1, key_type_name, encoded_key) == 3) { 544 | return true; 545 | } 546 | return false; 547 | } 548 | 549 | tbKeyType getKeyTypeFromName(char* key_type_name) { 550 | tbKeyType i; 551 | for (i = 0; i < TB_INVALID_KEY_TYPE; i++) { 552 | if (!strcmp(tbGetKeyTypeName(i), key_type_name)) { 553 | return i; 554 | } 555 | } 556 | return TB_INVALID_KEY_TYPE; 557 | } 558 | 559 | void saveOracleKeys(Oracle* oracle) { 560 | FILE* file = fopen("key_vault", "w"); 561 | if (file == NULL) { 562 | printf("Could not open key_vault for writing\n"); 563 | exit(1); 564 | } 565 | /* The format for each key, one per line, is: 566 | etld+1 key_type_name base64Encoded(openssl_formatted_key_pair) */ 567 | Key* key; 568 | for (key = oracle->first_key; key != NULL; key = key->next_key) { 569 | fprintf(file, "%s %s %s\n", key->etld_plus1, 570 | tbGetKeyTypeName(key->key_type), key->encoded_key); 571 | } 572 | fclose(file); 573 | } 574 | 575 | Cookie* findCookie(CookieJar* cookie_jar, char* hostname, char* name) { 576 | Cookie* cookie; 577 | for (cookie = cookie_jar->first_cookie; cookie != NULL; 578 | cookie = cookie->next_cookie) { 579 | if (!strcasecmp(cookie->hostname, hostname) && 580 | !strcasecmp(cookie->name, name)) { 581 | return cookie; 582 | } 583 | } 584 | return NULL; 585 | } 586 | 587 | Cookie* createCookie(CookieJar* cookie_jar, char* hostname, char* name, 588 | char* value) { 589 | Cookie* cookie = findCookie(cookie_jar, hostname, name); 590 | if (cookie != NULL) { 591 | free(cookie->value); 592 | cookie->value = copystring(value); 593 | return cookie; 594 | } 595 | cookie = checkedCalloc(1, sizeof(Cookie)); 596 | cookie->hostname = copystring(hostname); 597 | cookie->name = copystring(name); 598 | cookie->value = copystring(value); 599 | cookie->next_cookie = cookie_jar->first_cookie; 600 | cookie_jar->first_cookie = cookie; 601 | return cookie; 602 | } 603 | 604 | bool readCookie(FILE* file, char* hostname, char* name, char* value) { 605 | /* Note: fscanf is unsafe and can cause buffer overflow. It is used for 606 | simplicity in this demo. */ 607 | if (fscanf(file, "%s %s %s\n", hostname, name, value) == 3) { 608 | return true; 609 | } 610 | return false; 611 | } 612 | 613 | /* The oracle models a secure vault for token binding keys. Ideally, it is 614 | hardware backed, for example in Android TEE, or Intel SGX, but on systems 615 | where there is no fast signing oracle, it might be a library run in a 616 | different sandbox. The important thing is to keep the private key 617 | inaccessible to apps and instead provide signing through an oracle. 618 | 619 | This function loads saved keys from a file named key_vault, to emulate a 620 | real hardware-backed or sandboxed signing oracle. */ 621 | Oracle* readOracleKeys(void) { 622 | Oracle* oracle = checkedCalloc(1, sizeof(Oracle)); 623 | FILE* file = fopen("key_vault", "r"); 624 | if (file == NULL) { 625 | /* No keys to read yet. */ 626 | return oracle; 627 | } 628 | /* The format for each key, one per line, is: 629 | etld+1 key_type_name base64Encoded(openssl_formatted_key_pair) */ 630 | char etld_plus1[kBufferSize]; 631 | char key_type_name[kBufferSize]; 632 | char encoded_key[kBufferSize]; 633 | while (readKey(file, etld_plus1, key_type_name, encoded_key)) { 634 | tbKeyType key_type = getKeyTypeFromName(key_type_name); 635 | createKey(oracle, etld_plus1, key_type, encoded_key); 636 | } 637 | fclose(file); 638 | return oracle; 639 | } 640 | 641 | CookieJar* readCookieJar(void) { 642 | CookieJar* cookie_jar = checkedCalloc(1, sizeof(CookieJar)); 643 | FILE* file = fopen("cookie_jar", "r"); 644 | if (file == NULL) { 645 | /* No cookies to read yet. */ 646 | return cookie_jar; 647 | } 648 | /* The format for a cookie in the jar is hostname name value. */ 649 | char hostname[kBufferSize]; 650 | char name[kBufferSize]; 651 | char value[kBufferSize]; 652 | while (readCookie(file, hostname, name, value)) { 653 | createCookie(cookie_jar, hostname, name, value); 654 | } 655 | fclose(file); 656 | return cookie_jar; 657 | } 658 | 659 | void saveCookieJar(CookieJar* cookie_jar) { 660 | FILE* file = fopen("cookie_jar", "w"); 661 | if (file == NULL) { 662 | printf("Could not open cookie_jar for writing\n"); 663 | exit(1); 664 | } 665 | /* The format for a cookie in the jar is hostname name value. */ 666 | Cookie* cookie; 667 | for (cookie = cookie_jar->first_cookie; cookie != NULL; 668 | cookie = cookie->next_cookie) { 669 | fprintf(file, "%s %s %s\n", cookie->hostname, cookie->name, cookie->value); 670 | } 671 | fclose(file); 672 | } 673 | 674 | char* addCookie(char* p, Cookie* cookie) { 675 | /* Note: sprintf as used here is unsafe, and can result in buffer overflow. 676 | It is used for simplicity in this demo. */ 677 | return p + sprintf(p, "Cookie: %s=%s\r\n", cookie->name, cookie->value); 678 | } 679 | 680 | char* findCookies(CookieJar* cookie_jar, char* hostname) { 681 | char cookies[kBufferSize]; 682 | char* p = cookies; 683 | Cookie* cookie; 684 | for (cookie = cookie_jar->first_cookie; cookie != NULL; 685 | cookie = cookie->next_cookie) { 686 | if (!strcasecmp(cookie->hostname, hostname)) { 687 | p = addCookie(p, cookie); 688 | } 689 | } 690 | return copystring(cookies); 691 | } 692 | 693 | void processSetCookies(CookieJar* cookie_jar, char* response, char* hostname) { 694 | char* header_end = strstr(response, "\r\n\r\n"); 695 | char* p = strcasestr(response, "cookie"); 696 | while (p != NULL && (header_end == NULL || p < header_end)) { 697 | p += strlen("cookie"); 698 | while (*p == ':' || *p == ' ' || *p == '\t') { 699 | p++; 700 | } 701 | char* name_start = p; 702 | char* name_end = strchr(p, '='); 703 | char name[kBufferSize]; 704 | memcpy(name, name_start, name_end - name_start); 705 | name[name_end - name_start] = '\0'; 706 | p = name_end + 1; 707 | char* value_start = p; 708 | while (*p != '\n' && *p != '\r' && *p != '\0') { 709 | p++; 710 | } 711 | char* value_end = p; 712 | char value[kBufferSize]; 713 | memcpy(value, value_start, value_end - value_start); 714 | value[value_end - value_start] = '\0'; 715 | createCookie(cookie_jar, hostname, name, value); 716 | p = strcasestr(p, "cookie"); 717 | } 718 | } 719 | 720 | /* This client example sends */ 721 | int main(int argc, char** argv) { 722 | if (argc < 3) { 723 | usage(); 724 | } 725 | HttpRequestType type = parseType(argv[1]); 726 | if (type == HTTP_UNKNOWN) { 727 | usage(); 728 | } 729 | char* hostname; 730 | char* path; 731 | int port; 732 | parseHostPathAndPort(argv[2], &hostname, &path, &port); 733 | Connection* connection = openConnection(hostname, port); 734 | if (connection == NULL) { 735 | printf("Could not connect to %s:%u\n", hostname, port); 736 | return 1; 737 | } 738 | if (connection->key_type == TB_INVALID_KEY_TYPE) { 739 | printf("The server did not negotiate token binding\n"); 740 | return 1; 741 | } 742 | char* etld_plus1 = getETLDPlus1(hostname); 743 | printf("eTLD+1=%s\n", etld_plus1); 744 | Oracle* oracle = readOracleKeys(); 745 | CookieJar* cookie_jar = readCookieJar(); 746 | char* tbheader = 747 | generateTokenBindingHeader(connection, oracle, etld_plus1, NULL); 748 | char* cookies = findCookies(cookie_jar, hostname); 749 | char* response; 750 | if (type == HTTP_GET) { 751 | response = 752 | sendGetRequest(connection, hostname, port, path, tbheader, cookies); 753 | } else { 754 | if (argc < 4) { 755 | usage(); 756 | } 757 | char* variables = argv[3]; 758 | response = sendPostRequest(connection, hostname, port, path, tbheader, 759 | variables, cookies); 760 | } 761 | printf("Got response:\n%s\n", response); 762 | processSetCookies(cookie_jar, response, hostname); 763 | saveCookieJar(cookie_jar); 764 | saveOracleKeys(oracle); 765 | closeConnection(connection); 766 | return 0; 767 | } 768 | -------------------------------------------------------------------------------- /example/custom_ext_resume.patch: -------------------------------------------------------------------------------- 1 | From bc95dcfda999e7980bf1fdf20ec5df5384147118 Mon Sep 17 00:00:00 2001 2 | From: Bill Cox 3 | Date: Sat, 5 Nov 2016 10:45:58 -0700 4 | Subject: [PATCH] Enable custom extensions on resume 5 | 6 | --- 7 | ssl/t1_lib.c | 2 +- 8 | 1 file changed, 1 insertion(+), 1 deletion(-) 9 | 10 | diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c 11 | index 86833d8..6ed3342 100644 12 | --- a/ssl/t1_lib.c 13 | +++ b/ssl/t1_lib.c 14 | @@ -2175,7 +2175,7 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET *pkt, int *al) 15 | * callback and record the extension number so that an appropriate 16 | * ServerHello may be later returned. 17 | */ 18 | - else if (!s->hit) { 19 | + else { 20 | if (custom_ext_parse(s, 1, type, PACKET_data(&extension), 21 | PACKET_remaining(&extension), al) <= 0) 22 | return 0; 23 | -- 24 | 2.8.0.rc3.226.g39d4020 25 | 26 | -------------------------------------------------------------------------------- /example/runtest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "removing old key_vault and cookie_jar" 3 | rm -f ./key_vault ./cookie_jar 4 | ./server & 5 | serverPID=$! 6 | echo "Started server" 7 | ./client get https://localhost:40000 8 | echo "************************This should have redirected us to /login" 9 | ./client get https://localhost:40000/login 10 | echo "************************This should have shown us the login form" 11 | ./client post https://localhost:40000/login "user=john&pw=password" 12 | echo "************************This should have submitted our login form and redirected us to /user, while setting a bound auth cookie" 13 | ./client get https://localhost:40000/user 14 | echo "************************This should have shown us the user page" 15 | kill $serverPID 16 | if [ "`grep auth cookie_jar`" == "" ]; then 17 | echo "TEST FAILED" 18 | else 19 | echo "TEST PASSED" 20 | fi 21 | -------------------------------------------------------------------------------- /example/server.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | Author: waywardgeek@gmail.com (Bill Cox) 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 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | 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 and 14 | limitations under the License. */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #define PORT 40000 30 | 31 | /* Buffer size for reading/writing requests/responses. */ 32 | const size_t kBufferSize = 16384; /* Matches SSL's buffer size */ 33 | 34 | const char* kCookieSecret = "super-secret-password"; 35 | 36 | char* login_page = 37 | "

LOGIN

\n" 38 | "
\n" 39 | " Username:
\n" 40 | " Password:

\n" 42 | " \n" 43 | "
"; 44 | 45 | typedef enum { 46 | HTTP_POST, 47 | HTTP_GET, 48 | HTTP_UNKNOWN, 49 | } HttpRequestType; 50 | 51 | struct Connection_st { 52 | SSL_CTX* ctx; 53 | SSL* ssl; 54 | int port; 55 | int listener_fd; 56 | int client_fd; 57 | tbCache* tokbind_cache; 58 | tbKeyType key_type; 59 | }; 60 | 61 | typedef struct Connection_st Connection; 62 | 63 | struct Cookie_st { 64 | uint64_t userID; 65 | uint64_t tokbind_id_hash; 66 | }; 67 | 68 | typedef struct Cookie_st Cookie; 69 | 70 | void* checkedCalloc(size_t num, size_t size) { 71 | void* p = calloc(num, size); 72 | if (p == NULL) { 73 | printf("Out of memory\n"); 74 | exit(1); 75 | } 76 | return p; 77 | } 78 | 79 | char* copystring(char* source) { 80 | size_t len = strlen(source); 81 | char* dest = checkedCalloc(len + 1, sizeof(char)); 82 | strcpy(dest, source); 83 | return dest; 84 | } 85 | 86 | /* Listen on port. If that is in use, add one and try again. */ 87 | Connection* createListeningConnection(int port) { 88 | Connection* connection = checkedCalloc(1, sizeof(struct Connection_st)); 89 | connection->listener_fd = socket(AF_INET, SOCK_STREAM, 0); 90 | struct sockaddr_in addr; 91 | addr.sin_family = AF_INET; 92 | addr.sin_port = htons(port); 93 | addr.sin_addr.s_addr = htonl(INADDR_ANY); 94 | while (bind(connection->listener_fd, (struct sockaddr*)&addr, sizeof(addr)) < 95 | 0) { 96 | printf("Could not bind socket on port %u\n", port); 97 | port++; 98 | addr.sin_port = htons(port); 99 | } 100 | connection->port = port; 101 | printf("Listening on port %u\n", port); 102 | if (listen(connection->listener_fd, 1) < 0) { 103 | printf("Could not listen on port %u\n", port); 104 | exit(1); 105 | } 106 | OPENSSL_init_ssl( 107 | OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); 108 | SSL_CTX* ctx = SSL_CTX_new(TLS_server_method()); 109 | connection->ctx = ctx; 110 | SSL_CTX_use_PrivateKey_file(ctx, "test.key", SSL_FILETYPE_PEM); 111 | SSL_CTX_use_certificate_file(ctx, "test.crt", SSL_FILETYPE_PEM); 112 | EC_KEY* ecdh_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); 113 | SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE); 114 | SSL_CTX_set_tmp_ecdh(ctx, ecdh_key); 115 | EC_KEY_free(ecdh_key); 116 | if (!tbTLSLibInit()) { 117 | printf("Failed to initialize TLS token binding negotiation library\n"); 118 | exit(1); 119 | } 120 | if (!tbEnableTLSTokenBindingNegotiation(ctx)) { 121 | printf("Failed to enable TLS token binding negotiation\n"); 122 | exit(1); 123 | } 124 | return connection; 125 | } 126 | 127 | /* Wait for a new connection on the port. */ 128 | void waitForConnection(Connection* connection) { 129 | struct sockaddr_in addr; 130 | socklen_t len = sizeof(addr); 131 | connection->client_fd = 132 | accept(connection->listener_fd, (struct sockaddr*)&addr, &len); 133 | if (connection->client_fd < 0) { 134 | printf("Could not accept connection\n"); 135 | exit(1); 136 | } 137 | printf("Got new connection\n"); 138 | } 139 | 140 | void negotiateTLS(Connection* connection) { 141 | connection->ssl = SSL_new(connection->ctx); 142 | SSL_set_fd(connection->ssl, connection->client_fd); 143 | SSL_accept(connection->ssl); 144 | connection->tokbind_cache = tbCacheCreate(); 145 | } 146 | 147 | void closeConnection(Connection* connection) { 148 | printf("closing connection\n"); 149 | tbCacheDestroy(connection->tokbind_cache); 150 | connection->tokbind_cache = NULL; 151 | SSL_shutdown(connection->ssl); 152 | SSL_free(connection->ssl); 153 | connection->ssl = NULL; 154 | close(connection->client_fd); 155 | connection->client_fd = -1; 156 | } 157 | 158 | /* This function assumes the entire request is sent in one packet, and not 159 | split across SSL_read calls. Also, the maximum request size is limited to 160 | kBufferSize. */ 161 | char* readRequest(Connection* connection) { 162 | char buffer[kBufferSize]; 163 | int num_bytes = SSL_read(connection->ssl, buffer, kBufferSize); 164 | buffer[num_bytes] = '\0'; 165 | if (num_bytes <= 0) { 166 | printf("Could not read with return val %u\n", num_bytes); 167 | return NULL; 168 | } 169 | printf("Here is the message:\n%s\n", buffer); 170 | return copystring(buffer); 171 | } 172 | 173 | HttpRequestType getRequestType(char* request) { 174 | if (!strncmp(request, "GET", 3)) { 175 | return HTTP_GET; 176 | } else if (!strncmp(request, "POST", 4)) { 177 | return HTTP_POST; 178 | } 179 | return HTTP_UNKNOWN; 180 | } 181 | 182 | char* getRequestPath(char* request) { 183 | char* p = strchr(request, ' '); 184 | if (p == NULL) { 185 | return NULL; 186 | } 187 | char* start = p + 1; 188 | char* end = strchr(start, ' '); 189 | if (end == NULL) { 190 | return NULL; 191 | } 192 | char path[kBufferSize]; 193 | memcpy(path, start, end - start); 194 | path[end - start] = '\0'; 195 | printf("Found path %s\n", path); 196 | return copystring(path); 197 | } 198 | 199 | char* findRequestHeader(char* request, char* name) { 200 | char* header_end = strstr(request, "\r\n\r\n"); 201 | char* p = strcasestr(request, name); 202 | if (p == NULL || (header_end != NULL && p > header_end)) { 203 | return NULL; 204 | } 205 | p += strlen(name); 206 | while (*p == ':' || *p == ' ' || *p == '\t') { 207 | p++; 208 | } 209 | char* start = p; 210 | while (*p != '\n' && *p != '\r' && *p != '\0') { 211 | p++; 212 | } 213 | char* end = p; 214 | if (end == start) { 215 | return NULL; 216 | } 217 | /* The + 1 adds a '\0' at the end of the string. */ 218 | char header[kBufferSize]; 219 | memcpy(header, start, end - start); 220 | header[end - start] = '\0'; 221 | return copystring(header); 222 | } 223 | 224 | char* findRequestCookie(char* request, char* name) { 225 | char* header_end = strstr(request, "\r\n\r\n"); 226 | char* p = strcasestr(request, name); 227 | if (p == NULL || (header_end != NULL && p > header_end)) { 228 | return NULL; 229 | } 230 | p += strlen(name); 231 | while (*p == '=' || *p == ' ' || *p == '\t') { 232 | p++; 233 | } 234 | char* start = p; 235 | while (*p != '\n' && *p != '\r' && *p != '\0') { 236 | p++; 237 | } 238 | char* end = p; 239 | if (end == start) { 240 | return NULL; 241 | } 242 | /* The + 1 adds a '\0' at the end of the string. */ 243 | char cookie[kBufferSize]; 244 | memcpy(cookie, start, end - start); 245 | cookie[end - start] = '\0'; 246 | return copystring(cookie); 247 | } 248 | 249 | /* Hash the cookie secret and the TokenBindingID to get a 64-bit hash that we 250 | can embed in the cookie. Hashing with the cookie secret helps defend 251 | against collision attacks. */ 252 | uint64_t hashTokenBindingIDAndCookieSecret(uint8_t* tokbind_id, 253 | size_t tokbind_id_len) { 254 | uint8_t hash[SHA256_DIGEST_LENGTH]; 255 | SHA256_CTX sha256; 256 | SHA256_Init(&sha256); 257 | SHA256_Update(&sha256, tokbind_id, tokbind_id_len); 258 | SHA256_Update(&sha256, kCookieSecret, strlen(kCookieSecret)); 259 | SHA256_Final(hash, &sha256); 260 | uint64_t hash64 = 0; 261 | size_t i; 262 | for (i = 0; i < sizeof(uint64_t); i++) { 263 | hash64 = (hash64 << 8) | hash[i]; 264 | } 265 | return hash64; 266 | } 267 | 268 | /* This is where we generate and encrypt the auth cookie. Here, I just hash 269 | the secret cookie password with the TokenBindingID to generate the auth 270 | cookie, but you probably would prefer to create an auth cookie with a 271 | UserID, etc. */ 272 | char* generateAuthCookie(uint8_t* tokbind_id, size_t tokbind_id_len) { 273 | Cookie cookie; 274 | /* A real app would do a real userID lookup and verify the password here. */ 275 | cookie.userID = 1234; 276 | cookie.tokbind_id_hash = 277 | hashTokenBindingIDAndCookieSecret(tokbind_id, tokbind_id_len); 278 | /* This is where you would encrypt the cookie, but in this example we 279 | don't. */ 280 | size_t cookie_string_len = CalculateBase64EscapedLen(sizeof(Cookie), false); 281 | char* cookie_string = checkedCalloc(cookie_string_len + 1, sizeof(char)); 282 | WebSafeBase64Escape((void*)&cookie, sizeof(Cookie), cookie_string, 283 | cookie_string_len, false); 284 | return cookie_string; 285 | } 286 | 287 | bool verifyAuthCookie(char* auth_cookie, uint8_t* tokbind_id, 288 | size_t tokbind_id_len) { 289 | Cookie cookie; 290 | size_t cookie_len = 291 | WebSafeBase64Unescape(auth_cookie, (void*)&cookie, sizeof(Cookie)); 292 | if (cookie_len != sizeof(Cookie)) { 293 | return false; 294 | } 295 | /* This is where you would decrypt the cookie, but we don't in this 296 | example. */ 297 | if (cookie.userID != 1234) { 298 | /* Wrong user */ 299 | return false; 300 | } 301 | uint64_t tokbind_id_hash = 302 | hashTokenBindingIDAndCookieSecret(tokbind_id, tokbind_id_len); 303 | if (cookie.tokbind_id_hash != tokbind_id_hash) { 304 | /* Wrong TokenBindingID */ 305 | return false; 306 | } 307 | return true; 308 | } 309 | 310 | char* getSetCookieString(char* cookie) { 311 | char buf[kBufferSize]; 312 | if (cookie == NULL) { 313 | buf[0] = '\0'; 314 | } else { 315 | sprintf(buf, "Set-Cookie: auth=%s\r\n", cookie); 316 | } 317 | return copystring(buf); 318 | } 319 | 320 | void respond(Connection* connection, char* title, char* message, char* cookie) { 321 | char header[kBufferSize]; 322 | sprintf(header, "\r\n%s\r\n\r\n", 323 | title); 324 | char* footer = "\r\n\r\n\r\n"; 325 | char* set_cookie_string = getSetCookieString(cookie); 326 | char response[kBufferSize]; 327 | int response_len = sprintf(response, 328 | "HTTP/1.1 200 OK\r\n" 329 | "Server: example_server\r\n" 330 | "%s" /* Cookie string */ 331 | "Content-Length: %lu\r\n" 332 | "Content-Type: text/html\r\n" 333 | "Connection: Close\r\n" 334 | "\r\n" 335 | "%s" /* header */ 336 | "%s" /* message */ 337 | "%s", /* footer */ 338 | set_cookie_string, 339 | strlen(header) + strlen(message) + strlen(footer), 340 | header, message, footer); 341 | free(set_cookie_string); 342 | printf("Sending:\n%s\n", response); 343 | if (SSL_write(connection->ssl, response, response_len) <= 0) { 344 | printf("Unable to write to SSL connection\n"); 345 | exit(1); 346 | } 347 | } 348 | 349 | void redirect(Connection* connection, char* dest, char* cookie) { 350 | char response[kBufferSize]; 351 | int response_len = 352 | sprintf(response, 353 | "HTTP/1.1 303 See Other\n" 354 | "Connection: Close\r\n" 355 | "Location: https://localhost:%u%s\r\n%s\r\n", 356 | connection->port, dest, getSetCookieString(cookie)); 357 | printf("Sending redirect:\n%s\n", response); 358 | if (SSL_write(connection->ssl, response, response_len) <= 0) { 359 | printf("Unable to write to SSL connection\n"); 360 | exit(1); 361 | } 362 | } 363 | 364 | void respondNotFound(Connection* connection) { 365 | char* response = 366 | "HTTP/1.1 404 Not Found\n" 367 | "Connection: Close\r\n\r\n"; 368 | int response_len = strlen(response); 369 | printf("Sending redirect:\n%s\n", response); 370 | if (SSL_write(connection->ssl, response, response_len) <= 0) { 371 | printf("Unable to write to SSL connection\n"); 372 | exit(1); 373 | } 374 | } 375 | 376 | bool getRequestTokenBindingID(char* request, Connection* connection, 377 | uint8_t** out_tokbind_id, 378 | size_t* out_tokbind_id_len) { 379 | char* tbheader = findRequestHeader(request, "sec-token-binding"); 380 | if (tbheader == NULL) { 381 | printf("No token binding header in request\n"); 382 | return NULL; 383 | } 384 | printf("Found token binding header: %s\n", tbheader); 385 | uint8_t* referred_tokbind_id; 386 | size_t referred_tokbind_id_len; 387 | size_t maxlen = strlen(tbheader); 388 | char* message = checkedCalloc(maxlen, sizeof(char)); 389 | size_t message_len = WebSafeBase64Unescape(tbheader, message, maxlen); 390 | free(tbheader); 391 | if (message_len == 0) { 392 | printf("Could not base64-unencode token binding header\n"); 393 | return false; 394 | } 395 | if (tbCacheMessageAlreadyVerified( 396 | connection->tokbind_cache, (uint8_t*)message, message_len, 397 | out_tokbind_id, out_tokbind_id_len, &referred_tokbind_id, 398 | &referred_tokbind_id_len)) { 399 | if (referred_tokbind_id != NULL) { 400 | printf( 401 | "Token binding header with referred TokenBindingID was found in the " 402 | "cache\n"); 403 | } else { 404 | printf("Token binding header was found in the cache\n"); 405 | } 406 | return true; 407 | } 408 | uint8_t ekm[TB_HASH_LEN]; 409 | if (!tbGetEKM(connection->ssl, ekm)) { 410 | printf("Unable to get EKM from TLS connection\n"); 411 | exit(1); 412 | } 413 | if (!tbCacheVerifyTokenBindingMessage( 414 | connection->tokbind_cache, (uint8_t*)message, message_len, 415 | connection->key_type, ekm, out_tokbind_id, out_tokbind_id_len, 416 | &referred_tokbind_id, &referred_tokbind_id_len)) { 417 | printf("Bad token binding header\n"); 418 | return false; 419 | } 420 | printf("Verified token binding header\n"); 421 | return true; 422 | } 423 | 424 | /* Return true if the request is authenticated with an auth cookie. */ 425 | bool requestAuthenticated(char* request, uint8_t* tokbind_id, 426 | size_t tokbind_id_len) { 427 | char* auth_cookie = findRequestCookie(request, "auth"); 428 | if (auth_cookie == NULL) { 429 | return false; 430 | } 431 | bool verified = verifyAuthCookie(auth_cookie, tokbind_id, tokbind_id_len); 432 | free(auth_cookie); 433 | return verified; 434 | } 435 | 436 | void processRequest(char* request, Connection* connection) { 437 | uint8_t* tokbind_id; 438 | size_t tokbind_id_len; 439 | if (connection->key_type == TB_INVALID_KEY_TYPE) { 440 | respond(connection, "Warning", "Token binding not negotiated", NULL); 441 | return; 442 | } 443 | if (!getRequestTokenBindingID(request, connection, &tokbind_id, 444 | &tokbind_id_len)) { 445 | respond(connection, "Warning", "No token binding header", NULL); 446 | return; 447 | } 448 | char* path = getRequestPath(request); 449 | HttpRequestType type = getRequestType(request); 450 | bool authenticated = 451 | requestAuthenticated(request, tokbind_id, tokbind_id_len); 452 | if (strcasecmp(path, "/login") && !authenticated) { 453 | redirect(connection, "/login", NULL); 454 | } else if (!strcasecmp(path, "/")) { 455 | redirect(connection, "/user", NULL); 456 | } else if (!strcasecmp(path, "/login")) { 457 | if (type == HTTP_POST) { 458 | char* auth_cookie = generateAuthCookie(tokbind_id, tokbind_id_len); 459 | redirect(connection, "/user", auth_cookie); 460 | } else { 461 | respond(connection, "Login", login_page, NULL); 462 | } 463 | } else if (!strcasecmp(path, "/user")) { 464 | if (getRequestType(request) != HTTP_GET) { 465 | respond(connection, "Unsupported Request", 466 | "We only respond to GET and POST requests", NULL); 467 | return; 468 | } 469 | char* auth_cookie = findRequestCookie(request, "auth"); 470 | if (auth_cookie == NULL) { 471 | respond(connection, "Login", login_page, NULL); 472 | return; 473 | } 474 | if (!verifyAuthCookie(auth_cookie, tokbind_id, tokbind_id_len)) { 475 | respond(connection, "Login", login_page, NULL); 476 | return; 477 | } 478 | respond( 479 | connection, "Home Page", 480 | "We verified your auth cookie. You have reached your user home " 481 | "page. If this were a real web application, you would see something " 482 | "cool now.", 483 | NULL); 484 | } else { 485 | respondNotFound(connection); 486 | } 487 | free(path); 488 | } 489 | 490 | int main(int argc, char** argv) { 491 | (void)argc; 492 | (void)argv; 493 | uint64_t rand_seed = 0; /* This prevents collision attacks on the cache. */ 494 | RAND_seed(&rand_seed, sizeof(uint64_t)); 495 | tbCacheLibInit( 496 | rand_seed); /* Make sure to pass a true random number in your system */ 497 | Connection* connection = createListeningConnection(PORT); 498 | do { 499 | waitForConnection(connection); 500 | negotiateTLS(connection); 501 | tbKeyType key_type = TB_INVALID_KEY_TYPE; 502 | if (!tbTokenBindingEnabled(connection->ssl, &key_type)) { 503 | printf("Connection failed to negotiate token binding\n"); 504 | } else { 505 | printf("Connection negotiated token binding with key type %s\n", 506 | tbGetKeyTypeName(key_type)); 507 | } 508 | connection->key_type = key_type; 509 | char* request = readRequest(connection); 510 | while (request != NULL) { 511 | processRequest(request, connection); 512 | closeConnection(connection); 513 | waitForConnection(connection); 514 | negotiateTLS(connection); 515 | free(request); 516 | request = readRequest(connection); 517 | } 518 | closeConnection(connection); 519 | } while (true); 520 | return 0; 521 | } 522 | -------------------------------------------------------------------------------- /example/test.crt: -------------------------------------------------------------------------------- 1 | # Generated by 2 | # openssl req -new -x509 -key test.key -out test.crt -days 10000 3 | 4 | -----BEGIN CERTIFICATE----- 5 | MIICWDCCAcGgAwIBAgIJAOBbDQA5V2gSMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV 6 | BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX 7 | aWRnaXRzIFB0eSBMdGQwHhcNMTUwNzA4MjA0MTIzWhcNNDIxMTIzMjA0MTIzWjBF 8 | MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 9 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB 10 | gQCw6lIUH2uBqc0e0GhCSNwyXLhhU3hYd9gko7TO2MrFPzsVqUiG4ilO5TjoNVFy 11 | ZUpKSKVrbKW7DnqX7Zdol2cgpbZ/P22IvXfRg1/M7imUn63lWxo2uPBGHyYBtKHu 12 | fuadicz3jtOaerDaLGEtIcc59bR2Y0mUJEq4DVWjmRwkowIDAQABo1AwTjAdBgNV 13 | HQ4EFgQUs/jLdEP/VHyDVn8eyEBeJ3tihjEwHwYDVR0jBBgwFoAUs/jLdEP/VHyD 14 | Vn8eyEBeJ3tihjEwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOBgQBjBPHe 15 | nSCF5ChWvKFSdcdhLDyW3nrZslNbyqy0HH+IKeVZXr9tbKFoml7FFE0ujbAvaMET 16 | GnZCM6GnPijsPoYd7ZRSH/oZDPlNhkNjE7bMg9+bSQMFtettfOMAR+TGQ93+WbPa 17 | cgDlrnf7wt+m107ugChDaTW8nBpuPUeJpVswEg== 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /example/test.key: -------------------------------------------------------------------------------- 1 | # Generated by 2 | # openssl genrsa 1024 3 | 4 | -----BEGIN RSA PRIVATE KEY----- 5 | MIICXQIBAAKBgQCw6lIUH2uBqc0e0GhCSNwyXLhhU3hYd9gko7TO2MrFPzsVqUiG 6 | 4ilO5TjoNVFyZUpKSKVrbKW7DnqX7Zdol2cgpbZ/P22IvXfRg1/M7imUn63lWxo2 7 | uPBGHyYBtKHufuadicz3jtOaerDaLGEtIcc59bR2Y0mUJEq4DVWjmRwkowIDAQAB 8 | AoGAZZ/UUjbSPINbjo3MtfbAdjWIvG+EO91rzXA0VnV11ORDfmflf4W+kNFfZRPr 9 | mzjP+efNDe41LURd65PRW4VM+SBLnkcoh9WiolynOmV4PzCDa4vu/TOrP58UboKW 10 | 5ond4OsndNJ+q5HG3kRopWs8ywuJD3ebZ+WeprIOTC+Th8ECQQDqvajResOrZmk2 11 | qGcZ7VIHE/j8AMIhU63O94jEV3jkSpIOnFkGCNS7en77Vn7llotNS6igZJKFm7xK 12 | CppX6dBzAkEAwPABzzZ3PAWhJ3RCV0VMxxHS6nxx5RcLRmcKCL+UBdUGba6j/Qm6 13 | ai4E1YS+xiMs1wfhb7/d914uclm5EVM/EQJBAN8zRVEhxOm17/bru1T4xhU61XVw 14 | lyYvNcXksmPWtHmFukkwOqm1PemhuRntDa3qLjxp40XXO0bVZPyWBlnvVacCQGok 15 | 4Gn2ZfyQgEtra4AFsSqnxKiaVDKbzekqkNulQjM/Mi0vciqoqnT14lTxld79xRJ7 16 | 1GudW+lWWCky8Vz1tnECQQDLaF8X5Ngs7Y1heW5MSbS1kYzewt56amKl9oL5a8Dr 17 | R/CmOGvADN9+HB5NP6ACae/rbYd94P+/MGE/2w3OrSfo 18 | -----END RSA PRIVATE KEY----- 19 | -------------------------------------------------------------------------------- /example/unescape.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | Author: waywardgeek@gmail.com (Bill Cox) 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 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | 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 and 14 | limitations under the License. */ 15 | 16 | /* This is a simple utility to translate web-base64 to hex. It also prints the 17 | status for verifying a token binding header. It generally fails signature 18 | verification, since the EKM is not available. */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "token_bind_server.h" 26 | 27 | static void printHex(char *label, char* message, size_t message_len) { 28 | printf("%s:", label); 29 | size_t i; 30 | for (i = 0; i < message_len; i++) { 31 | printf(" %02x", (uint8_t)message[i]); 32 | } 33 | printf("\n"); 34 | } 35 | 36 | static bool readLine(char* line) { 37 | int pos = 0; 38 | int c; 39 | while ((c = getchar()) != '\n' && c != EOF) { 40 | line[pos++] = c; 41 | } 42 | line[pos] = '\0'; 43 | return c != EOF; 44 | } 45 | 46 | int main() { 47 | tbCacheLibInit(0xdeadbeef); 48 | tbCache* tokbind_cache = tbCacheCreate(); 49 | char line[1024]; 50 | char buf[1024]; 51 | uint8_t ekm[32] = {0,}; 52 | while(readLine(line)) { 53 | size_t len = WebSafeBase64Unescape(line, buf, 1024); 54 | printHex("TB header", buf, len); 55 | uint8_t* tokbind_id; 56 | uint8_t* referred_tokbind_id; 57 | size_t tokbind_id_len, referred_tokbind_id_len; 58 | tbCacheVerifyTokenBindingMessage( 59 | tokbind_cache, (uint8_t*)buf, len, 60 | TB_ECDSAP256, ekm, &tokbind_id, &tokbind_id_len, 61 | &referred_tokbind_id, &referred_tokbind_id_len); 62 | printf("status: %s\n", 63 | tbCacheGetStatusString(tbCacheGetStatus(tokbind_cache))); 64 | } 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /tb_bytestring.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Google Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. */ 14 | 15 | /* This file was copied from BoringSSL and stripped of functions not used by the 16 | * token binding libraries. */ 17 | 18 | #ifndef TOKEN_BIND_CSRC_TB_BYTESTRING_H 19 | #define TOKEN_BIND_CSRC_TB_BYTESTRING_H 20 | 21 | /* If using BoringSSL, this will now be defined. This file must be included 22 | * after the source file includes a public OpenSSL header, such as 23 | * or */ 24 | #ifdef OPENSSL_IS_BORINGSSL 25 | 26 | /* Use BoringSSL's versions of these functions. */ 27 | 28 | #include 29 | 30 | #define tbCBS CBS 31 | #define tbCBS_init CBS_init 32 | #define tbCBS_data CBS_data 33 | #define tbCBS_len CBS_len 34 | #define tbCBS_get_u8 CBS_get_u8 35 | #define tbCBS_get_u16 CBS_get_u16 36 | #define tbCBS_get_bytes CBS_get_bytes 37 | #define tbCBS_get_u8_length_prefixed CBS_get_u8_length_prefixed 38 | #define tbCBS_get_u16_length_prefixed CBS_get_u16_length_prefixed 39 | #define tbCBS_ASN1_INTEGER CBS_ASN1_INTEGER 40 | #define tbCBS_ASN1_SEQUENCE CBS_ASN1_SEQUENCE 41 | #define tbCBS_ASN1_CONSTRUCTED CBS_ASN1_CONSTRUCTED 42 | #define tbCBS_get_asn1 CBS_get_asn1 43 | #define tbCBS_get_asn1_element CBS_get_asn1_element 44 | #define tbCBS_get_any_asn1_element CBS_get_any_asn1_element 45 | #define tbCBB CBB 46 | #define tbCBB_init CBB_init 47 | #define tbCBB_cleanup CBB_cleanup 48 | #define tbCBB_finish CBB_finish 49 | #define tbCBB_flush CBB_flush 50 | #define tbCBB_data CBB_data 51 | #define tbCBB_len CBB_len 52 | #define tbCBB_add_u8_length_prefixed CBB_add_u8_length_prefixed 53 | #define tbCBB_add_u16_length_prefixed CBB_add_u16_length_prefixed 54 | #define tbCBB_add_asn1 CBB_add_asn1 55 | #define tbCBB_add_bytes CBB_add_bytes 56 | #define tbCBB_add_u8 CBB_add_u8 57 | #define tbCBB_add_u16 CBB_add_u16 58 | 59 | #else 60 | 61 | /* When using OpenSSL, link in these functions copied from BoringSSL. */ 62 | 63 | /* Bytestrings are used for parsing and building TLS and ASN.1 messages. 64 | * 65 | * A "tbCBS" (CRYPTO ByteString) represents a string of bytes in memory and 66 | * provides utility functions for safely parsing length-prefixed structures 67 | * like TLS and ASN.1 from it. 68 | * 69 | * A "tbCBB" (CRYPTO ByteBuilder) is a memory buffer that grows as needed and 70 | * provides utility functions for building length-prefixed messages. */ 71 | 72 | #include 73 | #include 74 | 75 | /* CRYPTO ByteString */ 76 | 77 | struct tbcbs_st { 78 | const uint8_t *data; 79 | size_t len; 80 | }; 81 | 82 | typedef struct tbcbs_st tbCBS; 83 | 84 | /* tbCBS_init sets |cbs| to point to |data|. It does not take ownership of 85 | * |data|. */ 86 | void tbCBS_init(tbCBS *cbs, const uint8_t *data, size_t len); 87 | 88 | /* tbCBS_data returns a pointer to the contents of |cbs|. */ 89 | const uint8_t *tbCBS_data(const tbCBS *cbs); 90 | 91 | /* tbCBS_len returns the number of bytes remaining in |cbs|. */ 92 | size_t tbCBS_len(const tbCBS *cbs); 93 | 94 | /* tbCBS_get_u8 sets |*out| to the next uint8_t from |cbs| and advances |cbs|. 95 | * It returns one on success and zero on error. */ 96 | int tbCBS_get_u8(tbCBS *cbs, uint8_t *out); 97 | 98 | /* tbCBS_get_u16 sets |*out| to the next, big-endian uint16_t from |cbs| and 99 | * advances |cbs|. It returns one on success and zero on error. */ 100 | int tbCBS_get_u16(tbCBS *cbs, uint16_t *out); 101 | 102 | /* tbCBS_get_bytes sets |*out| to the next |len| bytes from |cbs| and advances 103 | * |cbs|. It returns one on success and zero on error. */ 104 | int tbCBS_get_bytes(tbCBS *cbs, tbCBS *out, size_t len); 105 | 106 | /* tbCBS_get_u8_length_prefixed sets |*out| to the contents of an 8-bit, 107 | * length-prefixed value from |cbs| and advances |cbs| over it. It returns one 108 | * on success and zero on error. */ 109 | int tbCBS_get_u8_length_prefixed(tbCBS *cbs, tbCBS *out); 110 | 111 | /* tbCBS_get_u16_length_prefixed sets |*out| to the contents of a 16-bit, 112 | * big-endian, length-prefixed value from |cbs| and advances |cbs| over it. It 113 | * returns one on success and zero on error. */ 114 | int tbCBS_get_u16_length_prefixed(tbCBS *cbs, tbCBS *out); 115 | 116 | /* Parsing ASN.1 */ 117 | 118 | /* The following values are tag numbers for UNIVERSAL elements. */ 119 | #define tbCBS_ASN1_INTEGER 0x2 120 | #define tbCBS_ASN1_SEQUENCE (0x10 | tbCBS_ASN1_CONSTRUCTED) 121 | 122 | /* tbCBS_ASN1_CONSTRUCTED may be ORed into a tag to toggle the constructed 123 | * bit. |tbCBS| and |tbCBB| APIs consider the constructed bit to be part of the 124 | * tag. */ 125 | #define tbCBS_ASN1_CONSTRUCTED 0x20 126 | 127 | /* tbCBS_get_asn1 sets |*out| to the contents of DER-encoded, ASN.1 element (not 128 | * including tag and length bytes) and advances |cbs| over it. The ASN.1 element 129 | * must match |tag_value|. It returns one on success and zero on error. 130 | * 131 | * Tag numbers greater than 30 are not supported (i.e. short form only). */ 132 | int tbCBS_get_asn1(tbCBS *cbs, tbCBS *out, unsigned tag_value); 133 | 134 | /* tbCBS_get_asn1_element acts like |tbCBS_get_asn1| but |out| will include the 135 | * ASN.1 header bytes too. */ 136 | int tbCBS_get_asn1_element(tbCBS *cbs, tbCBS *out, unsigned tag_value); 137 | 138 | /* tbCBS_get_any_asn1_element sets |*out| to contain the next ASN.1 element from 139 | * |*cbs| (including header bytes) and advances |*cbs|. It sets |*out_tag| to 140 | * the tag number and |*out_header_len| to the length of the ASN.1 header. Each 141 | * of |out|, |out_tag|, and |out_header_len| may be NULL to ignore the value. 142 | * 143 | * Tag numbers greater than 30 are not supported (i.e. short form only). */ 144 | int tbCBS_get_any_asn1_element(tbCBS *cbs, tbCBS *out, unsigned *out_tag, 145 | size_t *out_header_len); 146 | 147 | /* CRYPTO ByteBuilder. 148 | * 149 | * |tbCBB| objects allow one to build length-prefixed serialisations. A |tbCBB| 150 | * object is associated with a buffer and new buffers are created with 151 | * |tbCBB_init|. Several |tbCBB| objects can point at the same buffer when a 152 | * length-prefix is pending, however only a single |tbCBB| can be 'current' at 153 | * any one time. For example, if one calls |tbCBB_add_u8_length_prefixed| then 154 | * the new |tbCBB| points at the same buffer as the original. But if the 155 | * original |tbCBB| is used then the length prefix is written out and the new 156 | * |tbCBB| must not be used again. 157 | * 158 | * If one needs to force a length prefix to be written out because a |tbCBB| is 159 | * going out of scope, use |tbCBB_flush|. If an operation on a |tbCBB| fails, it 160 | * is in an undefined state and must not be used except to call |tbCBB_cleanup|. 161 | */ 162 | 163 | struct tb_cbb_buffer_st { 164 | uint8_t *buf; 165 | size_t len; /* The number of valid bytes. */ 166 | size_t cap; /* The size of buf. */ 167 | /* One iff |buf| is owned by this object. If not then |buf| cannot be 168 | * resized. */ 169 | char can_resize; 170 | /* One iff there was an error writing to this tbCBB. All future operations will 171 | * fail. */ 172 | char error; 173 | }; 174 | 175 | struct tbcbb_st; 176 | typedef struct tbcbb_st tbCBB; 177 | 178 | struct tbcbb_st { 179 | struct tb_cbb_buffer_st *base; 180 | /* child points to a child tbCBB if a length-prefix is pending. */ 181 | tbCBB *child; 182 | /* offset is the number of bytes from the start of |base->buf| to this 183 | * |tbCBB|'s pending length prefix. */ 184 | size_t offset; 185 | /* pending_len_len contains the number of bytes in this |tbCBB|'s pending 186 | * length-prefix, or zero if no length-prefix is pending. */ 187 | uint8_t pending_len_len; 188 | char pending_is_asn1; 189 | /* is_top_level is true iff this is a top-level |tbCBB| (as opposed to a child 190 | * |tbCBB|). Top-level objects are valid arguments for |tbCBB_finish|. */ 191 | char is_top_level; 192 | }; 193 | 194 | /* tbCBB_init initialises |cbb| with |initial_capacity|. Since a |tbCBB| grows 195 | * as needed, the |initial_capacity| is just a hint. It returns one on success 196 | * or zero on error. */ 197 | int tbCBB_init(tbCBB *cbb, size_t initial_capacity); 198 | 199 | /* tbCBB_cleanup frees all resources owned by |cbb| and other |tbCBB| objects 200 | * writing to the same buffer. This should be used in an error case where a 201 | * serialisation is abandoned. 202 | * 203 | * This function can only be called on a "top level" |tbCBB|, i.e. one 204 | * initialised with |tbCBB_init| or |tbCBB_init_fixed|, or a |tbCBB| set to the 205 | * zero state with |tbCBB_zero|. */ 206 | void tbCBB_cleanup(tbCBB *cbb); 207 | 208 | /* tbCBB_finish completes any pending length prefix and sets |*out_data| to a 209 | * malloced buffer and |*out_len| to the length of that buffer. The caller 210 | * takes ownership of the buffer and, unless the buffer was fixed with 211 | * |tbCBB_init_fixed|, must call |OPENSSL_free| when done. 212 | * 213 | * It can only be called on a "top level" |tbCBB|, i.e. one initialised with 214 | * |tbCBB_init| or |tbCBB_init_fixed|. It returns one on success and zero on 215 | * error. */ 216 | int tbCBB_finish(tbCBB *cbb, uint8_t **out_data, size_t *out_len); 217 | 218 | /* tbCBB_flush causes any pending length prefixes to be written out and any 219 | * child |tbCBB| objects of |cbb| to be invalidated. It returns one on success 220 | * or zero on error. */ 221 | int tbCBB_flush(tbCBB *cbb); 222 | 223 | /* tbCBB_data returns a pointer to the bytes written to |cbb|. It does not flush 224 | * |cbb|. The pointer is valid until the next operation to |cbb|. 225 | * 226 | * To avoid unfinalized length prefixes, it is a fatal error to call this on a 227 | * tbCBB with any active children. */ 228 | const uint8_t *tbCBB_data(const tbCBB *cbb); 229 | 230 | /* tbCBB_len returns the number of bytes written to |cbb|. It does not flush 231 | * |cbb|. 232 | * 233 | * To avoid unfinalized length prefixes, it is a fatal error to call this on a 234 | * tbCBB with any active children. */ 235 | size_t tbCBB_len(const tbCBB *cbb); 236 | 237 | /* tbCBB_add_u8_length_prefixed sets |*out_contents| to a new child of |cbb|. 238 | * The data written to |*out_contents| will be prefixed in |cbb| with an 8-bit 239 | * length. It returns one on success or zero on error. */ 240 | int tbCBB_add_u8_length_prefixed(tbCBB *cbb, tbCBB *out_contents); 241 | 242 | /* tbCBB_add_u16_length_prefixed sets |*out_contents| to a new child of |cbb|. 243 | * The data written to |*out_contents| will be prefixed in |cbb| with a 16-bit, 244 | * big-endian length. It returns one on success or zero on error. */ 245 | int tbCBB_add_u16_length_prefixed(tbCBB *cbb, tbCBB *out_contents); 246 | 247 | /* tbCBB_add_asn1 sets |*out_contents| to a |tbCBB| into which the contents of 248 | * an ASN.1 object can be written. The |tag| argument will be used as the tag 249 | * for the object. Passing in |tag| number 31 will return in an error since only 250 | * single octet identifiers are supported. It returns one on success or zero on 251 | * error. */ 252 | int tbCBB_add_asn1(tbCBB *cbb, tbCBB *out_contents, unsigned tag); 253 | 254 | /* tbCBB_add_bytes appends |len| bytes from |data| to |cbb|. It returns one on 255 | * success and zero otherwise. */ 256 | int tbCBB_add_bytes(tbCBB *cbb, const uint8_t *data, size_t len); 257 | 258 | /* tbCBB_add_u8 appends an 8-bit number from |value| to |cbb|. It returns one on 259 | * success and zero otherwise. */ 260 | int tbCBB_add_u8(tbCBB *cbb, uint8_t value); 261 | 262 | /* tbCBB_add_u16 appends a 16-bit, big-endian number from |value| to |cbb|. It 263 | * returns one on success and zero otherwise. */ 264 | int tbCBB_add_u16(tbCBB *cbb, uint16_t value); 265 | 266 | #endif /* OPENSSL_IS_BORING_SSL */ 267 | 268 | #endif /* TOKEN_BIND_CSRC_TB_BYTESTRING_*/ 269 | -------------------------------------------------------------------------------- /token_bind_client.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | Author: waywardgeek@gmail.com (Bill Cox) 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 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | 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 and 14 | limitations under the License. */ 15 | 16 | #include "token_bind_client.h" 17 | 18 | #include 19 | #include "tb_bytestring.h" 20 | 21 | /* Add a token binding to the token binding message. */ 22 | static bool buildTokenBinding(tbCBB* message_contents, 23 | uint8_t token_binding_type, 24 | const uint8_t* tokbind_id, size_t tokbind_id_len, 25 | const uint8_t* signature, size_t signature_len) { 26 | if (tokbind_id == NULL || tokbind_id_len == 0) { 27 | return false; 28 | } 29 | tbCBB sig_cbb; 30 | if (!tbCBB_add_u8(message_contents, token_binding_type) || 31 | !tbCBB_add_bytes(message_contents, tokbind_id, tokbind_id_len) || 32 | !tbCBB_add_u16_length_prefixed(message_contents, &sig_cbb) || 33 | !tbCBB_add_bytes(&sig_cbb, signature, signature_len) || 34 | /* No extensions, so just add u16 zero. */ 35 | !tbCBB_add_u16(message_contents, 0) || 36 | !tbCBB_flush(message_contents)) { 37 | return false; 38 | } 39 | return true; 40 | } 41 | 42 | bool tbBuildTokenBindingMessage(const uint8_t* tokbind_id, 43 | size_t tokbind_id_len, const uint8_t* signature, 44 | size_t signature_len, uint8_t** out_message, 45 | size_t* out_message_len) { 46 | tbCBB tokbind_message, message_contents; 47 | if (!tbCBB_init(&tokbind_message, 0) || out_message == NULL || 48 | out_message_len == NULL || 49 | !tbCBB_add_u16_length_prefixed(&tokbind_message, &message_contents) || 50 | !buildTokenBinding(&message_contents, TB_PROVIDED, tokbind_id, 51 | tokbind_id_len, signature, signature_len) || 52 | !tbCBB_finish(&tokbind_message, out_message, out_message_len)) { 53 | tbCBB_cleanup(&tokbind_message); 54 | return false; 55 | } 56 | return true; 57 | } 58 | 59 | bool tbBuildReferredTokenBindingMessage( 60 | const uint8_t* tokbind_id, size_t tokbind_id_len, const uint8_t* signature, 61 | size_t signature_len, const uint8_t* referred_tokbind_id, 62 | size_t referred_tokbind_id_len, const uint8_t* referred_signature, 63 | size_t referred_signature_len, uint8_t** out_message, 64 | size_t* out_message_len) { 65 | tbCBB tokbind_message, message_contents; 66 | if (!tbCBB_init(&tokbind_message, 0) || out_message == NULL || 67 | out_message_len == NULL || 68 | !tbCBB_add_u16_length_prefixed(&tokbind_message, &message_contents) || 69 | !buildTokenBinding(&message_contents, TB_PROVIDED, tokbind_id, 70 | tokbind_id_len, signature, signature_len) || 71 | !buildTokenBinding(&message_contents, TB_REFERRED, referred_tokbind_id, 72 | referred_tokbind_id_len, referred_signature, 73 | referred_signature_len) || 74 | !tbCBB_finish(&tokbind_message, out_message, out_message_len)) { 75 | tbCBB_cleanup(&tokbind_message); 76 | return false; 77 | } 78 | return true; 79 | } 80 | 81 | bool tbEncodeKey(tbKeyType key_type, const EVP_PKEY* key, 82 | uint8_t** out_tokbind_id, size_t* out_tokbind_id_len) { 83 | int key_len = i2d_PublicKey((EVP_PKEY*)key, NULL); 84 | if (key_len < 1) { 85 | return false; 86 | } 87 | uint8_t* buf = malloc(key_len * sizeof(uint8_t)); 88 | if (buf == NULL) { 89 | return false; 90 | } 91 | uint8_t* bufp = buf; 92 | if (i2d_PublicKey((EVP_PKEY*)key, &bufp) != key_len) { 93 | free(buf); 94 | return false; /* Should never happen. */ 95 | } 96 | if (!tbConvertDerKeyToTokenBindingID(buf, key_len, key_type, out_tokbind_id, 97 | out_tokbind_id_len)) { 98 | free(buf); 99 | return false; 100 | } 101 | free(buf); 102 | return true; 103 | } 104 | -------------------------------------------------------------------------------- /token_bind_client.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | Author: waywardgeek@gmail.com (Bill Cox) 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 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | 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 and 14 | limitations under the License. */ 15 | 16 | #ifndef TOKEN_BIND_CSRC_TOKEN_BIND_CLIENT_H_ 17 | #define TOKEN_BIND_CSRC_TOKEN_BIND_CLIENT_H_ 18 | 19 | /* This library implements client-side functionality of Token Binding, as 20 | specified in: https://datatracker.ietf.org/doc/draft-ietf-tokbind-protocol 21 | 22 | In essence, Token Binding is the next version of Channel ID. It is on track 23 | to become an IETF standard. */ 24 | 25 | #include 26 | #include "token_bind_common.h" 27 | 28 | /* tbBuildTokenBindingMessage can be called by an HTTP client to create a Token 29 | Binding Message in |out_message| which can be attached to an HTTP request to 30 | prove possession of the Token Binding private key. |signature| is a 31 | signature of |EKM|, using cipher suite specified by GetKeyType(tokbind_id). 32 | This builds a "provided" token binding. The caller is responsible for 33 | calling free on out_message. */ 34 | bool tbBuildTokenBindingMessage(const uint8_t* tokbind_id, 35 | size_t tokbind_id_len, const uint8_t* signature, 36 | size_t signature_len, uint8_t** out_message, 37 | size_t* out_message_len); 38 | 39 | /* tbBuildReferredTokenBindingMessage can be called by an HTTP client to create 40 | a Token Binding Message in |out_message| which can be attached to an HTTP 41 | request to prove possession of the Token Binding private key. |signature| is 42 | a signature of |EKM|, using cipher suite specified by GetKeyType(tokbind_id). 43 | This builds a "provided" token binding, and also a "referred" token binding. 44 | The caller takes ownership of |out_message|. */ 45 | bool tbBuildReferredTokenBindingMessage( 46 | const uint8_t* tokbind_id, size_t tokbind_id_len, const uint8_t* signature, 47 | size_t signature_len, const uint8_t* referred_tokbind_id, 48 | size_t referred_tokbind_id_len, const uint8_t* referred_signature, 49 | size_t referred_signature_len, uint8_t** out_message, 50 | size_t* out_message_len); 51 | 52 | /* EncodeKey converts a EVP_PKEY object to a TokenBindingID. The caller takes 53 | ownership of |out_tokbind_id|. */ 54 | bool tbEncodeKey(tbKeyType key_type, const EVP_PKEY* key, 55 | uint8_t** out_tokbind_id, size_t* out_tokbind_id_len); 56 | 57 | #endif /* TOKEN_BIND_CSRC_TOKEN_BIND_CLIENT_H_ */ 58 | -------------------------------------------------------------------------------- /token_bind_common.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | Author: waywardgeek@gmail.com (Bill Cox) 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 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | 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 and 14 | limitations under the License. */ 15 | 16 | #include "token_bind_common.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include "tb_bytestring.h" 22 | 23 | static const size_t kCoordSize = 32; 24 | 25 | /* IANA-assigned Token Binding TLS extension number */ 26 | static const int kTOKEN_BIND_EXTENSION_TYPE = 24; 27 | 28 | /* kHeaderSize is the number of bytes on the wire for this extension 29 | that preceed the list of key types. It is 3 bytes long. The first two 30 | encode the protocol version, and the third is the number of key types. */ 31 | static const size_t kHeaderSize = 3; 32 | static const size_t kMajorVersionPos = 0; /* Major version position */ 33 | static const size_t kMinorVersionPos = 1; /* Minor version position */ 34 | static const size_t kLengthPos = 2; /* Length field position */ 35 | 36 | static const size_t kECDSAP256RawKeyLen = 64; 37 | static const size_t kECDSAP256DerKeyLen = 65; 38 | static const size_t kECDSAP256TokenBindKeyLen = 68; 39 | static const uint8_t kUncompressedPoint = 4; 40 | 41 | /* client_major_version and client_minor_version are global variables used 42 | for testing version negotiation. */ 43 | int client_major_version = TB_MAJOR_VERSION; 44 | int client_minor_version = TB_MINOR_VERSION; 45 | 46 | /* The following globals are initialized when first used. They are needed to 47 | attach data to SSL objects. */ 48 | 49 | /* ssl_ctx_ex_data_index_key_types is used to save the preferred key types on 50 | the SSL context. */ 51 | static int ssl_ctx_ex_data_index_key_types = -1; 52 | /* ssl_ex_data_index_negotiated_key_type is used to save the negotiated key type 53 | on an SSL connection. */ 54 | static int ssl_ex_data_index_negotiated_key_type = -1; 55 | /* ssl_ex_data_index_negotiated_version is used to record the negotiated token 56 | binding version. */ 57 | static int ssl_ex_data_index_negotiated_version = -1; 58 | 59 | struct tbKeyTypeVect_st { 60 | uint8_t* key_types; 61 | uint8_t num_key_types; 62 | }; 63 | 64 | typedef struct tbKeyTypeVect_st tbKeyTypeVect; 65 | 66 | /* Destroy a key type vector. */ 67 | static void tbKeyTypeVectDestroy(tbKeyTypeVect* key_type_vect) { 68 | if (key_type_vect == NULL) { 69 | return; 70 | } 71 | if (key_type_vect->key_types != NULL) { 72 | free(key_type_vect->key_types); 73 | } 74 | free(key_type_vect); 75 | } 76 | 77 | /* Create a key type vector. */ 78 | static tbKeyTypeVect* tbKeyTypeVectCreate(const uint8_t* key_types, 79 | size_t num_key_types) { 80 | tbKeyTypeVect* key_type_vect = calloc(1, sizeof(tbKeyTypeVect)); 81 | if (key_type_vect == NULL) { 82 | return NULL; 83 | } 84 | if (num_key_types != 0) { 85 | key_type_vect->key_types = calloc(num_key_types, sizeof(uint8_t)); 86 | if (key_type_vect->key_types == NULL) { 87 | tbKeyTypeVectDestroy(key_type_vect); 88 | return NULL; 89 | } 90 | key_type_vect->num_key_types = num_key_types; 91 | memcpy(key_type_vect->key_types, key_types, 92 | num_key_types * sizeof(uint8_t)); 93 | } 94 | return key_type_vect; 95 | } 96 | 97 | /* Return true if the key type is in key_type_vect. */ 98 | static bool tbKeyTypeVectHasKeyType(const tbKeyTypeVect* key_type_vect, 99 | tbKeyType key_type) { 100 | return strchr((char*)(key_type_vect->key_types), key_type) != NULL; 101 | } 102 | 103 | /* Callback for cleaning up the tbKeyTypeVect we store on the SSL_CTX. */ 104 | static void freeKeyTypeVect(void* parent, void* ptr, CRYPTO_EX_DATA* ad, 105 | int idx, long argl, void* argp) { 106 | (void)parent; 107 | (void)ad; 108 | (void)idx; 109 | (void)argl; 110 | (void)argp; 111 | tbKeyTypeVectDestroy((tbKeyTypeVect*)ptr); 112 | } 113 | 114 | /* Initialize SSL extended data indexes. */ 115 | bool tbTLSLibInit(void) { 116 | if (ssl_ctx_ex_data_index_key_types == -1) { 117 | ssl_ctx_ex_data_index_key_types = SSL_CTX_get_ex_new_index( 118 | 0 /* opaque argument for callbacks */, 119 | NULL /* opaque argument for callbacks */, 120 | NULL /* no CRYPTO_EX_new callback */, 121 | NULL /* no CRYPTO_EX_DUP callback */, freeKeyTypeVect); 122 | if (ssl_ctx_ex_data_index_key_types < 0) { 123 | return false; 124 | } 125 | } 126 | if (ssl_ex_data_index_negotiated_key_type == -1) { 127 | ssl_ex_data_index_negotiated_key_type = 128 | SSL_get_ex_new_index(0 /* opaque argument for callbacks */, 129 | NULL /* opaque argument for callbacks */, 130 | NULL /* no CRYPTO_EX_new callback */, 131 | NULL /* no CRYPTO_EX_dup callback */, 132 | NULL /* no CRYPTO_EX_free callback */); 133 | if (ssl_ex_data_index_negotiated_key_type < 0) { 134 | return false; 135 | } 136 | } 137 | if (ssl_ex_data_index_negotiated_version == -1) { 138 | ssl_ex_data_index_negotiated_version = 139 | SSL_get_ex_new_index(0 /* opaque argument for callbacks */, 140 | NULL /* opaque argument for callbacks */, 141 | NULL /* no CRYPTO_EX_new callback */, 142 | NULL /* no CRYPTO_EX_dup callback */, 143 | NULL /* no CRYPTO_EX_free callback */); 144 | if (ssl_ex_data_index_negotiated_version < 0) { 145 | return false; 146 | } 147 | } 148 | return true; 149 | } 150 | 151 | /* GetContextKeyTypes retrieves the key types from the context. The context 152 | retains ownership of the key types vector. */ 153 | static tbKeyTypeVect* getContextKeyTypes(const SSL_CTX* ssl_ctx) { 154 | return SSL_CTX_get_ex_data(ssl_ctx, ssl_ctx_ex_data_index_key_types); 155 | } 156 | 157 | /* setContextKeyTypes set the acceptable key types on the context. The context 158 | creates its own copy of the key types. */ 159 | static void setContextKeyTypes(SSL_CTX* ssl_ctx, const uint8_t* key_types, 160 | size_t num_key_types) { 161 | tbKeyTypeVect* prev_key_types = getContextKeyTypes(ssl_ctx); 162 | if (prev_key_types != NULL) { 163 | tbKeyTypeVectDestroy(prev_key_types); 164 | } 165 | tbKeyTypeVect* key_types_vect = tbKeyTypeVectCreate(key_types, num_key_types); 166 | SSL_CTX_set_ex_data(ssl_ctx, ssl_ctx_ex_data_index_key_types, key_types_vect); 167 | } 168 | 169 | /* setNegotiatedKeyTypes sets the negotiated key types on |ssl|. The key types 170 | vector must remain valid for the entire life of |ssl_ctx|. */ 171 | static void setNegotiatedKeyType(SSL* ssl, tbKeyType key_type) { 172 | /* Add 1 to key_type so that the default of 0 will indicate that token 173 | binding was not negotiatted. */ 174 | SSL_set_ex_data(ssl, ssl_ex_data_index_negotiated_key_type, 175 | (void*)(uintptr_t)(key_type + 1)); 176 | } 177 | 178 | /* getNegotiatedKeyType retrieves the key types from the context. The key types 179 | vector is owned by the caller of SetContextKeyTypes. If token binding has 180 | not been negotiated, this will return TB_INVALID_KEY_TYPE. */ 181 | static tbKeyType getNegotiatedKeyType(const SSL* ssl) { 182 | void* ptrval = SSL_get_ex_data(ssl, ssl_ex_data_index_negotiated_key_type); 183 | intptr_t intval = (intptr_t)ptrval; 184 | if (intval == 0) { 185 | return TB_INVALID_KEY_TYPE; 186 | } 187 | return intval - 1; 188 | } 189 | 190 | /* SetNegotiatedVersion sets the negotiated extension major and minor version on 191 | |ssl|. */ 192 | static void setNegotiatedVersion(SSL* ssl, uint8_t major_version, 193 | uint8_t minor_version) { 194 | void* version = (void*)(uintptr_t)(major_version | (minor_version << 8)); 195 | SSL_set_ex_data(ssl, ssl_ex_data_index_negotiated_version, version); 196 | } 197 | 198 | /* getNegotiatedVersion retrieves the negotiated major and minor version from 199 | |ssl|. The major version number is written to out[0], and and the minor 200 | version number is written to out[1]. */ 201 | static void getNegotiatedVersion(SSL* ssl, uint8_t* out) { 202 | uintptr_t version = 203 | (uintptr_t)SSL_get_ex_data(ssl, ssl_ex_data_index_negotiated_version); 204 | out[0] = version; 205 | out[1] = version >> 8; 206 | } 207 | 208 | /* extensionAddServerCallback is called from BoringSSL to add the token binding 209 | extension to the TLS server hello packet. */ 210 | static int extensionAddServerCallback(SSL* ssl, unsigned ext_type, 211 | const uint8_t** out, size_t* out_len, 212 | int* al, void* arg) { 213 | (void)ext_type; 214 | (void)al; 215 | (void)arg; 216 | tbKeyType key_type = getNegotiatedKeyType(ssl); 217 | if (key_type >= TB_INVALID_KEY_TYPE) { 218 | /* Failed to negotiate a key type, so do not add the extension. */ 219 | return 0; 220 | } 221 | /* Add only the negotiated key type. */ 222 | *out_len = kHeaderSize + 1; 223 | uint8_t* buf = calloc(*out_len, sizeof(uint8_t)); 224 | if (buf == NULL) { 225 | return 0; 226 | } 227 | /* Sets the first two bytes to the major and minor version numbers. */ 228 | getNegotiatedVersion(ssl, buf); 229 | /* The number of keys sent by the server is always 1. */ 230 | buf[kLengthPos] = 1; 231 | buf[kHeaderSize] = key_type; 232 | *out = buf; 233 | return 1; 234 | } 235 | 236 | /* extensionAddClientCallback is called from BoringSSL to add the token binding 237 | extension to the TLS client hello packet. */ 238 | static int extensionAddClientCallback(SSL* ssl, unsigned ext_type, 239 | const uint8_t** out, size_t* out_len, 240 | int* al, void* arg) { 241 | (void)ext_type; 242 | (void)al; 243 | (void)arg; 244 | const tbKeyTypeVect* key_type_vect = getContextKeyTypes(SSL_get_SSL_CTX(ssl)); 245 | size_t num_key_types = key_type_vect->num_key_types; 246 | if (key_type_vect == NULL || num_key_types == 0 || 247 | num_key_types >= TB_INVALID_KEY_TYPE) { 248 | return 0; 249 | } 250 | *out_len = kHeaderSize + num_key_types; 251 | uint8_t* buf = calloc(*out_len, sizeof(uint8_t)); 252 | if (buf == NULL) { 253 | return 0; 254 | } 255 | /* Sets the first two bytes to the major and minor version numbers. */ 256 | setNegotiatedVersion(ssl, client_major_version, client_minor_version); 257 | /* Copy version to buf. */ 258 | getNegotiatedVersion(ssl, buf); 259 | /* Add key types from the context. */ 260 | buf[kLengthPos] = num_key_types; 261 | size_t i; 262 | for (i = 0; i < num_key_types; i++) { 263 | tbKeyType key_type = key_type_vect->key_types[i]; 264 | if (key_type >= TB_INVALID_KEY_TYPE) { 265 | free(buf); 266 | return 0; 267 | } 268 | buf[kHeaderSize + i] = key_type; 269 | } 270 | *out = buf; 271 | return 1; 272 | } 273 | 274 | /* Find the most acceptable common key type to the server. */ 275 | static tbKeyType findCommonKeyType(const SSL* ssl, const uint8_t* keys, 276 | size_t num_keys) { 277 | const tbKeyTypeVect* key_type_vect = getContextKeyTypes(SSL_get_SSL_CTX(ssl)); 278 | int i; 279 | for (i = 0; i < key_type_vect->num_key_types; i++) { 280 | tbKeyType key_type = key_type_vect->key_types[i]; 281 | if (key_type < TB_INVALID_KEY_TYPE && 282 | memchr(keys, key_type, num_keys) != NULL) { 283 | return key_type; 284 | } 285 | } 286 | return TB_INVALID_KEY_TYPE; 287 | } 288 | 289 | /* findMutalVersion chooses a mutual version. */ 290 | static bool findMutualVersion(const unsigned char* in, uint8_t version[2]) { 291 | version[0] = TB_MAJOR_VERSION; 292 | version[1] = TB_MINOR_VERSION; 293 | if (in[kMajorVersionPos] < version[kMajorVersionPos] || 294 | (in[kMajorVersionPos] == version[kMajorVersionPos] && 295 | in[kMinorVersionPos] < version[kMinorVersionPos])) { 296 | /* There are experimental versions of token binding support that use old 297 | formats, so require a version recent enough to be compliant with the 298 | current token binding spec. */ 299 | /* TODO(waywardgeek): When the min supported major version is > 0, comment 300 | this back in. */ 301 | if (/* in[kMajorVersionPos] < TB_MIN_SUPPORTED_MAJOR_VERSION || */ 302 | (in[kMajorVersionPos] == TB_MIN_SUPPORTED_MAJOR_VERSION && 303 | in[kMinorVersionPos] < TB_MIN_SUPPORTED_MINOR_VERSION)) { 304 | /* Refuse extension if version is below minSupportedVersion */ 305 | return false; 306 | } 307 | /* Downgrade to their supported version. */ 308 | version[kMajorVersionPos] = in[kMajorVersionPos]; 309 | version[kMinorVersionPos] = in[kMinorVersionPos]; 310 | } 311 | return true; 312 | } 313 | 314 | /* extensionParseServerCallback is called from BoringSSL to parse the token 315 | binding extension from the TLS hello packet. */ 316 | static int extensionParseServerCallback(SSL* ssl, unsigned ext_type, 317 | const unsigned char* in, size_t in_len, 318 | int* al, void* arg) { 319 | (void)ext_type; 320 | (void)arg; 321 | /* Verify the length field is valid. */ 322 | if (in_len < kHeaderSize || in[kLengthPos] + kHeaderSize != in_len) { 323 | *al = SSL3_AD_HANDSHAKE_FAILURE; 324 | return 0; /* Invalid format - this will terminate the connection. */ 325 | } 326 | uint8_t version[2]; 327 | if (!findMutualVersion(in, version)) { 328 | return 1; 329 | } 330 | setNegotiatedVersion(ssl, version[0], version[1]); 331 | /* Find the most acceptable supported key type. */ 332 | tbKeyType key_type = 333 | findCommonKeyType(ssl, in + kHeaderSize, in_len - kHeaderSize); 334 | /* If there were no common key types, disable Token Binding by not setting a 335 | negotiated key type on the SSL connection. */ 336 | if (key_type == TB_INVALID_KEY_TYPE) { 337 | return 1; 338 | } 339 | setNegotiatedKeyType(ssl, key_type); 340 | return 1; 341 | } 342 | 343 | /* extensionParseClientCallback is called from BoringSSL to parse the token 344 | binding extension from the TLS hello packet. */ 345 | static int extensionParseClientCallback(SSL* ssl, unsigned ext_type, 346 | const unsigned char* in, size_t in_len, 347 | int* al, void* arg) { 348 | (void)ext_type; 349 | (void)arg; 350 | /* Verify the length field is valid. */ 351 | if (in_len < kHeaderSize || in[kLengthPos] + kHeaderSize != in_len) { 352 | *al = SSL3_AD_HANDSHAKE_FAILURE; 353 | return 0; /* Invalid format - this will terminate the connection. */ 354 | } 355 | uint8_t version[2]; 356 | if (!findMutualVersion(in, version)) { 357 | return 1; 358 | } 359 | setNegotiatedVersion(ssl, version[0], version[1]); 360 | /* Abort if the server sent other than exactly one key type, or a version 361 | higher than ours. */ 362 | if (in_len != kHeaderSize + 1 || 363 | in[kMajorVersionPos] > version[kMajorVersionPos] || 364 | (in[kMajorVersionPos] == version[kMajorVersionPos] && 365 | in[kMinorVersionPos] > version[kMinorVersionPos])) { 366 | *al = SSL3_AD_HANDSHAKE_FAILURE; 367 | return 0; 368 | } 369 | tbKeyType key_type = in[kHeaderSize]; 370 | /* Abort if the server sent an unacceptable key type. */ 371 | const tbKeyTypeVect* key_type_vect = getContextKeyTypes(SSL_get_SSL_CTX(ssl)); 372 | if (!tbKeyTypeVectHasKeyType(key_type_vect, key_type)) { 373 | *al = SSL3_AD_HANDSHAKE_FAILURE; 374 | return 0; 375 | } 376 | /* Server key type is acceptable. */ 377 | setNegotiatedKeyType(ssl, key_type); 378 | return 1; 379 | } 380 | 381 | static void freeTLSOutData(SSL* ssl, unsigned extension_value, 382 | const uint8_t* out, void* add_arg) { 383 | (void)ssl; 384 | (void)extension_value; 385 | (void)add_arg; 386 | free((uint8_t*)out); 387 | } 388 | 389 | /* DER formatted integers can be negative, so when encoding a positive integer 390 | that starts with a 1 in the MSB of the most significant byte, a leading 0 391 | byte is added. To extract an unsigned int, we need to strip off any leading 392 | 0. |out_buf| is buf_len in size, so we fill the initial bytes with 0's if 393 | the DER encoded integer does not use all the bytes in the buffer. */ 394 | static bool getDerUint(tbCBS* uint_cbs, uint8_t* out_buf, size_t buf_len) { 395 | const uint8_t* data = tbCBS_data(uint_cbs); 396 | size_t length = tbCBS_len(uint_cbs); 397 | if (length == 0) { 398 | return false; 399 | } 400 | if (data[0] == 0) { 401 | data++; 402 | length--; 403 | } 404 | if (length > buf_len) { 405 | return false; 406 | } 407 | if (buf_len > length) { 408 | /* 0-pad the buffer. */ 409 | memset(out_buf, 0, buf_len - length); 410 | } 411 | memcpy(out_buf + (buf_len - length), data, length); 412 | return true; 413 | } 414 | 415 | /* Similarly, to encode an unsigned int, we need to add a leading 0 if the MSB 416 | is 1, and if we have leading 0-bytes, we strip them unless the next byte MSB 417 | is set, or if the only byte left is a 0. */ 418 | static bool addDerUint(tbCBB* uint_cbb, const uint8_t* in_buf, size_t length) { 419 | if (length == 0) { 420 | return false; 421 | } 422 | size_t pos = 0; 423 | /* Strip leading 0's except for the last byte. */ 424 | while (pos + 1 < length && in_buf[pos] == 0) { 425 | pos++; 426 | } 427 | if ((in_buf[pos] & 0x80) != 0) { 428 | tbCBB_add_u8(uint_cbb, 0); 429 | } 430 | return tbCBB_add_bytes(uint_cbb, in_buf + pos, length - pos); 431 | } 432 | 433 | void tbSetClientVersion(int major_version, int minor_version) { 434 | client_major_version = major_version; 435 | client_minor_version = minor_version; 436 | } 437 | 438 | bool tbEnableTLSTokenBindingNegotiation(SSL_CTX* ssl_ctx) { 439 | uint8_t key_types[] = {TB_ECDSAP256, TB_RSA2048_PSS, TB_RSA2048_PKCS15}; 440 | setContextKeyTypes(ssl_ctx, key_types, sizeof(key_types)); 441 | if (SSL_CTX_add_server_custom_ext( 442 | ssl_ctx, kTOKEN_BIND_EXTENSION_TYPE, &extensionAddServerCallback, 443 | freeTLSOutData, NULL, &extensionParseServerCallback, NULL) != 1) { 444 | return false; 445 | } 446 | if (SSL_CTX_add_client_custom_ext( 447 | ssl_ctx, kTOKEN_BIND_EXTENSION_TYPE, &extensionAddClientCallback, 448 | freeTLSOutData, NULL, &extensionParseClientCallback, NULL) != 1) { 449 | return false; 450 | } 451 | return true; 452 | } 453 | 454 | void tbUpdateKeyTypes(SSL_CTX* ssl_ctx, const uint8_t* key_types, 455 | size_t num_key_types) { 456 | setContextKeyTypes(ssl_ctx, key_types, num_key_types); 457 | } 458 | 459 | bool tbTokenBindingEnabled(const SSL* ssl, tbKeyType* out_key_type) { 460 | if (SSL_get_extms_support((SSL*)ssl) != 1) { 461 | return false; 462 | } 463 | tbKeyType key_type = getNegotiatedKeyType(ssl); 464 | if (key_type == TB_INVALID_KEY_TYPE) { 465 | return false; 466 | } 467 | if (out_key_type != NULL) { 468 | *out_key_type = key_type; 469 | } 470 | return true; 471 | } 472 | 473 | const char* tbGetKeyTypeName(tbKeyType key_type) { 474 | switch (key_type) { 475 | case TB_RSA2048_PKCS15: return "RSA-2048-PKCS1.5"; 476 | case TB_RSA2048_PSS: return "RSA-2048-PSS"; 477 | case TB_ECDSAP256: return "EC-DSA-P256"; 478 | case TB_INVALID_KEY_TYPE: return "invalid-key-type"; 479 | } 480 | return "unknown-key-type"; 481 | } 482 | 483 | tbKeyType tbGetKeyType(const uint8_t* tokbind_id, size_t tokbind_id_len) { 484 | if (tokbind_id_len == 0) { 485 | return TB_INVALID_KEY_TYPE; 486 | } 487 | return tokbind_id[0]; 488 | } 489 | 490 | bool tbGetEKM(const SSL* ssl, uint8_t out[TB_HASH_LEN]) { 491 | if (ssl == NULL) { 492 | return false; 493 | } 494 | static const char kLabel[] = "EXPORTER-Token-Binding"; 495 | return SSL_export_keying_material((SSL*)ssl, out, TB_HASH_LEN, kLabel, 496 | strlen(kLabel), NULL, 0, 0); 497 | } 498 | 499 | void tbGetDataToSign(uint8_t* ekm, tbKeyType key_type, bool referred, 500 | uint8_t** out_data, size_t* out_data_len) { 501 | *out_data_len = SHA256_DIGEST_LENGTH + 2; 502 | *out_data = calloc(*out_data_len, sizeof(uint8_t)); 503 | (*out_data)[0] = referred ? TB_REFERRED : TB_PROVIDED; 504 | (*out_data)[1] = key_type; 505 | memcpy(*out_data + 2, ekm, SHA256_DIGEST_LENGTH); 506 | } 507 | 508 | bool tbSetPadding(tbKeyType key_type, EVP_PKEY_CTX* key_ctx) { 509 | switch (key_type) { 510 | case TB_RSA2048_PKCS15: 511 | return EVP_PKEY_CTX_set_rsa_padding(key_ctx, RSA_PKCS1_PADDING); 512 | case TB_RSA2048_PSS: 513 | if (!EVP_PKEY_CTX_set_rsa_padding(key_ctx, RSA_PKCS1_PSS_PADDING)) { 514 | return false; 515 | } 516 | /* Set salt length to the digest length. */ 517 | return EVP_PKEY_CTX_set_rsa_pss_saltlen(key_ctx, -1) == 1; 518 | case TB_ECDSAP256: 519 | return true; 520 | default: 521 | return false; 522 | } 523 | } 524 | 525 | /* The "DER" format for keys is really just a format convention created by 526 | programmers over the years as they added support for new key types to OpenSSL 527 | and other libraries. The "DER" format for RSA keys is: 528 | 529 | SEQUENCE INTEGER(modulus) INTEGER(exponent) 530 | 531 | which is an actual DER encoding. For ECC keys, it is: 532 | 533 | 1-byte 0x4. 32-byte X, 32-byte Y 534 | 535 | which is a non-DER custom format. Token Binding uses custom formats. For 536 | RSA keys, it is: 537 | 538 | u8-key_type, u16-prefixed(u16-prefixed(modulus), u8-prefixed(exponent)) 539 | 540 | and for ECC keys: 541 | 542 | u8-key_type, u16-prefixed(u8-prefixed(32-byte X || 32-byt Y)) */ 543 | bool tbConvertDerKeyToTokenBindingID(const uint8_t* der_key, size_t der_key_len, 544 | tbKeyType key_type, 545 | uint8_t** out_tokbind_id, 546 | size_t* out_tokbind_id_len) { 547 | switch (key_type) { 548 | case TB_ECDSAP256: { 549 | if (der_key_len != kECDSAP256RawKeyLen + 1 || 550 | der_key[0] != kUncompressedPoint) { 551 | return false; 552 | } 553 | tbCBB tokbind_id, ec_point, raw_key; 554 | if (!tbCBB_init(&tokbind_id, kECDSAP256TokenBindKeyLen) || 555 | !tbCBB_add_u8(&tokbind_id, TB_ECDSAP256) || 556 | !tbCBB_add_u16_length_prefixed(&tokbind_id, &ec_point) || 557 | !tbCBB_add_u8_length_prefixed(&ec_point, &raw_key) || 558 | !tbCBB_add_bytes(&raw_key, der_key + 1, der_key_len - 1) || 559 | !tbCBB_finish(&tokbind_id, out_tokbind_id, out_tokbind_id_len)) { 560 | tbCBB_cleanup(&tokbind_id); 561 | return false; 562 | } 563 | return true; 564 | } 565 | case TB_RSA2048_PKCS15: 566 | case TB_RSA2048_PSS: { 567 | tbCBS key_cbs; 568 | tbCBS_init(&key_cbs, der_key, der_key_len); 569 | tbCBS sequence_cbs, modulus_cbs, exponent_cbs; 570 | if (!tbCBS_get_asn1(&key_cbs, &sequence_cbs, tbCBS_ASN1_SEQUENCE) || 571 | tbCBS_len(&key_cbs) != 0 || 572 | !tbCBS_get_asn1(&sequence_cbs, &modulus_cbs, tbCBS_ASN1_INTEGER) || 573 | !tbCBS_get_asn1(&sequence_cbs, &exponent_cbs, tbCBS_ASN1_INTEGER) || 574 | tbCBS_len(&sequence_cbs) != 0) { 575 | return false; 576 | } 577 | /* Skip any leading 0 on modulus and exponent. These are added in DER 578 | format when the MSB of an unsigned integer is 1. */ 579 | uint8_t val; 580 | while (tbCBS_data(&modulus_cbs)[0] == 0) { 581 | tbCBS_get_u8(&modulus_cbs, &val); 582 | } 583 | while (tbCBS_data(&exponent_cbs)[0] == 0) { 584 | tbCBS_get_u8(&exponent_cbs, &val); 585 | } 586 | tbCBB tokbind_id_cbb, key_content_cbb, modulus_cbb, exponent_cbb; 587 | if (!tbCBB_init(&tokbind_id_cbb, 0) || 588 | !tbCBB_add_u8(&tokbind_id_cbb, key_type) || 589 | !tbCBB_add_u16_length_prefixed(&tokbind_id_cbb, &key_content_cbb) || 590 | !tbCBB_add_u16_length_prefixed(&key_content_cbb, &modulus_cbb) || 591 | !tbCBB_add_bytes(&modulus_cbb, tbCBS_data(&modulus_cbs), 592 | tbCBS_len(&modulus_cbs)) || 593 | !tbCBB_add_u8_length_prefixed(&key_content_cbb, &exponent_cbb) || 594 | !tbCBB_add_bytes(&exponent_cbb, tbCBS_data(&exponent_cbs), 595 | tbCBS_len(&exponent_cbs)) || 596 | !tbCBB_finish(&tokbind_id_cbb, out_tokbind_id, out_tokbind_id_len)) { 597 | tbCBB_cleanup(&tokbind_id_cbb); 598 | return false; 599 | } 600 | return true; 601 | } 602 | case TB_INVALID_KEY_TYPE: 603 | break; 604 | } 605 | return false; /* Unsupported key type. */ 606 | } 607 | 608 | bool tbConvertTokenBindingIDToDerKey(const uint8_t* tokbind_id, 609 | size_t tokbind_id_len, 610 | tbKeyType* out_key_type, uint8_t** out_key, 611 | size_t* out_key_len) { 612 | *out_key = NULL; 613 | tbCBS tokbind_id_cbs; 614 | tbCBS_init(&tokbind_id_cbs, tokbind_id, tokbind_id_len); 615 | uint8_t key_type_val; 616 | if (!tbCBS_get_u8(&tokbind_id_cbs, &key_type_val)) { 617 | return false; 618 | } 619 | *out_key_type = key_type_val; 620 | tbCBS key_content_cbs; 621 | if (!tbCBS_get_u16_length_prefixed(&tokbind_id_cbs, &key_content_cbs) || 622 | tbCBS_len(&tokbind_id_cbs) != 0) { 623 | return false; 624 | } 625 | switch (key_type_val) { 626 | case TB_ECDSAP256: { 627 | tbCBS raw_key; 628 | if (tokbind_id_len != kECDSAP256TokenBindKeyLen || 629 | !tbCBS_get_u8_length_prefixed(&key_content_cbs, &raw_key) || 630 | tbCBS_len(&key_content_cbs) != 0 || 631 | tbCBS_len(&raw_key) != kECDSAP256RawKeyLen) { 632 | return false; 633 | } 634 | *out_key_len = kECDSAP256DerKeyLen; 635 | *out_key = calloc(*out_key_len, sizeof(uint8_t)); 636 | if (*out_key == NULL) { 637 | return false; 638 | } 639 | /* Add the uncompressed point 0x4 prefix */ 640 | (*out_key)[0] = kUncompressedPoint; 641 | /* Add X and Y. */ 642 | memcpy(*out_key + 1, tbCBS_data(&raw_key), kECDSAP256RawKeyLen); 643 | break; 644 | } 645 | case TB_RSA2048_PKCS15: 646 | case TB_RSA2048_PSS: { 647 | tbCBS modulus_cbs, exponent_cbs; 648 | if (!tbCBS_get_u16_length_prefixed(&key_content_cbs, &modulus_cbs) || 649 | !tbCBS_get_u8_length_prefixed(&key_content_cbs, &exponent_cbs) || 650 | tbCBS_len(&key_content_cbs) != 0) { 651 | return false; 652 | } 653 | tbCBB key_cbb, sequence_cbb, modulus_cbb, exponent_cbb; 654 | tbCBB_init(&key_cbb, 0); 655 | if (!tbCBB_add_asn1(&key_cbb, &sequence_cbb, tbCBS_ASN1_SEQUENCE) || 656 | !tbCBB_add_asn1(&sequence_cbb, &modulus_cbb, tbCBS_ASN1_INTEGER) || 657 | !addDerUint(&modulus_cbb, tbCBS_data(&modulus_cbs), 658 | tbCBS_len(&modulus_cbs)) || 659 | !tbCBB_add_asn1(&sequence_cbb, &exponent_cbb, tbCBS_ASN1_INTEGER) || 660 | !addDerUint(&exponent_cbb, tbCBS_data(&exponent_cbs), 661 | tbCBS_len(&exponent_cbs)) || 662 | !tbCBB_finish(&key_cbb, out_key, out_key_len)) { 663 | tbCBB_cleanup(&key_cbb); 664 | return false; 665 | } 666 | break; 667 | } 668 | default: 669 | return false; /* Unsupported key type */ 670 | } 671 | if (tbCBS_len(&tokbind_id_cbs) != 0) { 672 | if (*out_key != NULL) { 673 | free(*out_key); 674 | *out_key = NULL; 675 | } 676 | return false; 677 | } 678 | return true; 679 | } 680 | 681 | /* The "DER" format for signatures is really just a format convention created by 682 | programmers over the years as they added support for new key types to OpenSSL 683 | and other libraries. The "DER" format for RSA signatures is: 684 | 685 | 256-byte encrypted message 686 | 687 | which is not in DER format, unlike RSA keys. Note that there is no length 688 | prefix. ECC signatures have the form: 689 | 690 | SEQUENCE INTEGER(r) INTEGER(s) 691 | 692 | which is in DER format, unlike ECC keys. Token Binding uses custom formats. 693 | For RSA keys, it is: 694 | 695 | 256-byte encrypted message 696 | 697 | and for ECC keys: 698 | 699 | 32-byte R || 32-byte-S */ 700 | bool tbConvertDerSigToTokenBindingSig(const uint8_t* der_sig, 701 | size_t der_sig_len, tbKeyType key_type, 702 | uint8_t** out_sig, size_t* out_sig_len) { 703 | switch (key_type) { 704 | case TB_ECDSAP256: { 705 | tbCBS sig_cbs; 706 | tbCBS_init(&sig_cbs, der_sig, der_sig_len); 707 | *out_sig_len = 2 * kCoordSize; 708 | *out_sig = calloc(*out_sig_len, sizeof(uint8_t)); 709 | if (*out_sig == NULL) { 710 | return false; 711 | } 712 | tbCBS sequence_cbs, r_cbs, s_cbs; 713 | if (!tbCBS_get_asn1(&sig_cbs, &sequence_cbs, tbCBS_ASN1_SEQUENCE) || 714 | tbCBS_len(&sig_cbs) != 0 || 715 | !tbCBS_get_asn1(&sequence_cbs, &r_cbs, tbCBS_ASN1_INTEGER) || 716 | !getDerUint(&r_cbs, *out_sig, kCoordSize) || 717 | !tbCBS_get_asn1(&sequence_cbs, &s_cbs, tbCBS_ASN1_INTEGER) || 718 | !getDerUint(&s_cbs, *out_sig + kCoordSize, kCoordSize) || 719 | tbCBS_len(&sequence_cbs) != 0) { 720 | free(out_sig); 721 | return false; 722 | } 723 | return true; 724 | } 725 | case TB_RSA2048_PKCS15: 726 | case TB_RSA2048_PSS: { 727 | if (der_sig_len == 8) { 728 | return false; 729 | } 730 | *out_sig_len = der_sig_len; 731 | *out_sig = calloc(*out_sig_len, sizeof(uint8_t)); 732 | if (*out_sig == NULL) { 733 | return false; 734 | } 735 | memcpy(*out_sig, der_sig, der_sig_len); 736 | return true; 737 | } 738 | default: 739 | return false; /* Unsupported key type */ 740 | } 741 | return false; /* Cannot get here. */ 742 | } 743 | 744 | bool tbConvertTokenBindingSigToDerSig(const uint8_t* tb_sig, size_t tb_sig_len, 745 | tbKeyType key_type, uint8_t** out_sig, 746 | size_t* out_sig_len) { 747 | switch (key_type) { 748 | case TB_ECDSAP256: { 749 | if (tb_sig_len != 2 * kCoordSize) { 750 | return false; /* Invalid signature length */ 751 | } 752 | tbCBB sig_cbb; 753 | tbCBB_init(&sig_cbb, 0); 754 | tbCBB sequence_cbb, r_cbb, s_cbb; 755 | if (!tbCBB_add_asn1(&sig_cbb, &sequence_cbb, tbCBS_ASN1_SEQUENCE) || 756 | !tbCBB_add_asn1(&sequence_cbb, &r_cbb, tbCBS_ASN1_INTEGER) || 757 | !addDerUint(&r_cbb, tb_sig, kCoordSize) || 758 | !tbCBB_add_asn1(&sequence_cbb, &s_cbb, tbCBS_ASN1_INTEGER) || 759 | !addDerUint(&s_cbb, tb_sig + kCoordSize, kCoordSize) || 760 | !tbCBB_finish(&sig_cbb, out_sig, out_sig_len)) { 761 | tbCBB_cleanup(&sig_cbb); 762 | return false; 763 | } 764 | return true; 765 | } 766 | case TB_RSA2048_PKCS15: 767 | case TB_RSA2048_PSS: { 768 | if (tb_sig_len == 0) { 769 | return false; 770 | } 771 | *out_sig_len = tb_sig_len; 772 | *out_sig = calloc(*out_sig_len, sizeof(uint8_t)); 773 | if (*out_sig == NULL) { 774 | return false; 775 | } 776 | memcpy(*out_sig, tb_sig, tb_sig_len); 777 | return true; 778 | } 779 | default: 780 | return false; /* Unsupported key type */ 781 | } 782 | return false; /* Cannot get here. */ 783 | } 784 | 785 | void tbHashTokenBindingID(const uint8_t* tokbind_id, size_t tokbind_id_len, 786 | uint8_t hash_out[TB_HASH_LEN]) { 787 | SHA256_CTX sha256; 788 | SHA256_Init(&sha256); 789 | SHA256_Update(&sha256, tokbind_id, tokbind_id_len); 790 | SHA256_Final(hash_out, &sha256); 791 | } 792 | -------------------------------------------------------------------------------- /token_bind_common.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | Author: waywardgeek@gmail.com (Bill Cox) 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 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | 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 and 14 | limitations under the License. */ 15 | 16 | #ifndef TOKEN_BIND_CSRC_TOKEN_BIND_COMMON_H_ 17 | #define TOKEN_BIND_CSRC_TOKEN_BIND_COMMON_H_ 18 | 19 | /* This library implements client-side functionality of Token Binding, as 20 | specified in: https://datatracker.ietf.org/doc/draft-ietf-tokbind-protocol 21 | 22 | In essence, Token Binding is the next version of Channel ID. It is on track 23 | to become an IETF standard. */ 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | /* tbKeyType defines key parameter combos supported for Token Binding key 32 | pairs. */ 33 | typedef enum { 34 | TB_RSA2048_PKCS15 = 0, 35 | TB_RSA2048_PSS = 1, 36 | TB_ECDSAP256 = 2, 37 | TB_INVALID_KEY_TYPE = 255, 38 | } tbKeyType; 39 | 40 | /* Hash length of SHA256 in bytes */ 41 | #define TB_HASH_LEN 32 42 | 43 | /* These are used to parse Token Binding messages. */ 44 | enum tbTokenBindingType { TB_PROVIDED = 0, TB_REFERRED = 1 }; 45 | 46 | /* Major/minor version numbers for the version of Token Binding negotiated over 47 | TLS. The 0 major version means this is still experimental. */ 48 | #define TB_MAJOR_VERSION 0 49 | #define TB_MINOR_VERSION 18 50 | 51 | /* Require this version to ensure that clients do not send the old formats. */ 52 | #define TB_MIN_SUPPORTED_MAJOR_VERSION 0 53 | #define TB_MIN_SUPPORTED_MINOR_VERSION 10 54 | 55 | /* tbGetKeyType returns the key type of |tokbind_id|, which must be a public key 56 | encoded in token binding format (a TokenBindingID). Call tbEncodeKey to 57 | obtain this string. */ 58 | tbKeyType tbGetKeyType(const uint8_t* tokbind_id, size_t tokbind_id_len); 59 | 60 | /* Return a string representing the key type in ASCII. */ 61 | const char* tbGetKeyTypeName(tbKeyType key_type); 62 | 63 | /* tbTLSLibInit must be called once before calling 64 | tbEnableTLSTokenBindingNegotiation. True is returned if the library 65 | initializes successfully. */ 66 | bool tbTLSLibInit(void); 67 | 68 | /* tbEnableTLSTokenBindingNegotiation enables token binding on SSL connections 69 | created from |ssl_ctx|. It can be called for client and server connections. 70 | By default, all key types are enabled, with preference order: ECDSAP256, 71 | RSA2048_PSS, RSA2048_PKCS15. true is returned on success. tbTLSLibInit must 72 | be called before this function. */ 73 | bool tbEnableTLSTokenBindingNegotiation(SSL_CTX* ssl_ctx); 74 | 75 | /* tbUpdateKeyTypes is used to change the key types used on a context after 76 | tbEnableTLSTokenBindingNegotiation has been called. This can be used to 77 | change the key types without having to rebuild the context. */ 78 | void tbUpdateKeyTypes(SSL_CTX* ssl_ctx, const uint8_t* key_types, 79 | size_t num_key_types); 80 | 81 | /* tbTokenBindingEnabled returns the negotiated key type only if |ssl| 82 | has successfully negotiated token binding, and also the extended master 83 | secret extension was negotiated. If |out_key_type| is not nullptr, it will 84 | be set to the negotiated key type. */ 85 | bool tbTokenBindingEnabled(const SSL* ssl, tbKeyType* out_key_type); 86 | 87 | /* tbGetEKM generates a hash of the handshake messages to generate a unique 88 | value cryptographically bound to this SSL connection. If the handshake is 89 | not yet complete, then false is returned. The result is allocated in a new 90 | buffer which the caller owns. For the definition of EKM, see 91 | https://datatracker.ietf.org/doc/draft-ietf-tokbind-protocol */ 92 | bool tbGetEKM(const SSL* ssl, uint8_t out[TB_HASH_LEN]); 93 | 94 | /* tbGetDataToSign returns the data signed in a token binding message. It just 95 | * concatenates the input parameters to form the string to sign. */ 96 | void tbGetDataToSign(uint8_t* ekm, tbKeyType key_type, bool referred, 97 | uint8_t** out_data, size_t* out_data_len); 98 | 99 | /* The following conversion functions are provided to aid supporting token 100 | binding using libraries other than OpenSSL, which support the same key and 101 | signature formats as OpenSSL. In this context, "DER" encoding means OpenSSL 102 | encoding, which is only sometimes actual DER encoding. The latest token 103 | binding message format can be found at: 104 | https://tools.ietf.org/wg/tokbind/draft-ietf-tokbind-protocol */ 105 | 106 | /* tbConvertDerKeyToTokenBindingID creates a token binding formatted public key 107 | string (a "TokenBindingID") from a DER encoded byte array generated by 108 | popular tools such as OpenSSL. If the input is not formatted properly, false 109 | is returned. The result is allocated in a new buffer which the caller 110 | owns. */ 111 | bool tbConvertDerKeyToTokenBindingID(const uint8_t* der_key, size_t der_key_len, 112 | tbKeyType key_type, 113 | uint8_t** out_tokbind_id, 114 | size_t* out_tokbind_id_len); 115 | 116 | /* tbConvertTokenBindingIDToDerKey converts a token binding public key string 117 | (a TokenBindingID) to a DER encoded byte array which is compatible with 118 | popular tools such as OpenSSL. If the input is not formatted properly, false 119 | is returned. The result is allocated in a new buffer which the caller 120 | owns. */ 121 | bool tbConvertTokenBindingIDToDerKey(const uint8_t* tokbind_id, 122 | size_t tokbind_id_len, 123 | tbKeyType* out_key_type, uint8_t** out_key, 124 | size_t* out_key_len); 125 | 126 | /* tbConvertDerSigToTokenBindingSig creates a token binding formatted signature 127 | string from a DER encoded byte array generated by popular tools such as 128 | OpenSSL. If the input is not formatted properly, false is returned. The 129 | result is allocated in a new buffer which the caller owns. */ 130 | bool tbConvertDerSigToTokenBindingSig(const uint8_t* der_sig, 131 | size_t der_sig_len, tbKeyType key_type, 132 | uint8_t** out_sig, size_t* out_sig_len); 133 | 134 | /* tbConvertTokenBindingSigToDerSig converts a token binding signature string to 135 | a DER encoded byte array which is compatible with popular tools such as 136 | OpenSSL. If the input is not formatted properly, false is returned. The 137 | result is allocated in a new buffer which the caller owns. */ 138 | bool tbConvertTokenBindingSigToDerSig(const uint8_t* tb_sig, size_t tb_sig_len, 139 | tbKeyType key_type, uint8_t** out_sig, 140 | size_t* out_sig_len); 141 | 142 | /* tbSetClientVersion sets the version of token binding that will be negotiated 143 | during the TLS handshake. This applies globally, and is meant to be used 144 | only to test version negotation. */ 145 | void tbSetClientVersion(int major_version, int minor_version); 146 | 147 | /* tbSetPadding sets the padding type of |key_ctx| based on |key_type|. It does 148 | nothing for TB_ECDSAP256. It sets the padding scheme to PKCS15 for 149 | TB_RSA2048_PKCS15, and to PKCS15_PSS for TB_RSA2048_PSS, with salt length 150 | equal to the length of the hash digest, which is SHA256. */ 151 | bool tbSetPadding(tbKeyType key_type, EVP_PKEY_CTX* key_ctx); 152 | 153 | /* tbHashTokenBindingID computes the SHA256 hash digest of the token binding 154 | public key, which is a called TokenBindingID in the RFC Token Binding 155 | docs. */ 156 | void tbHashTokenBindingID(const uint8_t* tokbind_id, size_t tokbind_id_len, 157 | uint8_t hash_out[TB_HASH_LEN]); 158 | 159 | #endif /* TOKEN_BIND_CSRC_TOKEN_BIND_COMMON_H_ */ 160 | -------------------------------------------------------------------------------- /token_bind_server.c: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | Author: waywardgeek@gmail.com (Bill Cox) 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 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | 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 and 14 | limitations under the License. */ 15 | 16 | #include "token_bind_server.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "tb_bytestring.h" 23 | 24 | static uint64_t tbCacheNonce = 0; 25 | 26 | struct tbCacheStruct { 27 | uint64_t* message_hashes; 28 | uint32_t num_message_hashes; 29 | tbCacheStatus status; 30 | }; 31 | 32 | tbCacheStatus tbCacheGetStatus(tbCache* cache) { 33 | return cache->status; 34 | } 35 | 36 | /* extractTokenBindingID extracts the sub-string of |message_contents| that 37 | represent the public key (a TokenBindingID), starting with the KeyType byte. 38 | If |out_evp_key| is not NULL, then an EVP_PKEY is parsed from this string, 39 | and |out_evp_key| is set to own it. Note that this function will return true 40 | after setting |out_tokbind_id| when |out_evp_key| is NULL, even if 41 | out_tokbind_id does not represent a valid EVP_PKEY. This is useful behavior 42 | when we have cache hits for message headers, and just need to extract the 43 | TokenBindingID portion, without building an EVP_PKEY. The caller takes 44 | ownership of out_evp_key but not out_tokbind_id, which is a pointer into 45 | |message_contents|. */ 46 | static bool extractTokenBindingID(tbCBS* message_contents, 47 | tbKeyType* out_key_type, 48 | EVP_PKEY** out_evp_key, 49 | const uint8_t** out_tokbind_id, 50 | size_t* out_tokbind_id_len) { 51 | const uint8_t* tokbind_id_data = tbCBS_data(message_contents); 52 | uint8_t key_type; 53 | tbCBS tokbind_id_cbs; 54 | if (!tbCBS_get_u8(message_contents, &key_type) || 55 | !tbCBS_get_u16_length_prefixed(message_contents, &tokbind_id_cbs)) { 56 | return false; 57 | } 58 | /* The total length includes the key type and uint16_t length field */ 59 | size_t tokbind_id_len = tbCBS_len(&tokbind_id_cbs) + 3; 60 | *out_key_type = key_type; 61 | if (out_tokbind_id != NULL) { 62 | *out_tokbind_id = tokbind_id_data; 63 | *out_tokbind_id_len = tokbind_id_len; 64 | } 65 | if (out_evp_key == NULL) { 66 | return true; 67 | } 68 | uint8_t* der_key; 69 | size_t der_key_len; 70 | if (!tbConvertTokenBindingIDToDerKey(tokbind_id_data, tokbind_id_len, 71 | out_key_type, &der_key, &der_key_len)) { 72 | return false; 73 | } 74 | switch (*out_key_type) { 75 | case TB_ECDSAP256: { 76 | EC_KEY* eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); 77 | if (eckey == NULL) { 78 | free(der_key); 79 | return false; 80 | } 81 | const uint8_t* inp = der_key; 82 | if (o2i_ECPublicKey(&eckey, &inp, der_key_len) == NULL) { 83 | EC_KEY_free(eckey); 84 | free(der_key); 85 | return false; 86 | } 87 | free(der_key); 88 | EVP_PKEY* evp_key = EVP_PKEY_new(); 89 | if (evp_key == NULL) { 90 | return false; 91 | } 92 | EVP_PKEY_assign_EC_KEY(evp_key, eckey); 93 | *out_evp_key = evp_key; 94 | return true; 95 | } 96 | case TB_RSA2048_PKCS15: 97 | case TB_RSA2048_PSS: { 98 | const size_t kModulusSize = 2048; 99 | const uint8_t* inp = der_key; 100 | RSA* rsa = d2i_RSAPublicKey(NULL, &inp, der_key_len); 101 | free(der_key); 102 | if (rsa == NULL) { 103 | return false; 104 | } 105 | if ((unsigned)(8 * RSA_size(rsa)) != kModulusSize) { 106 | RSA_free(rsa); 107 | return false; 108 | } 109 | EVP_PKEY* evp_key = EVP_PKEY_new(); 110 | if (evp_key == NULL) { 111 | RSA_free(rsa); 112 | return false; 113 | } 114 | EVP_PKEY_assign_RSA(evp_key, rsa); 115 | *out_evp_key = evp_key; 116 | return true; 117 | } 118 | case TB_INVALID_KEY_TYPE: 119 | break; 120 | } 121 | /* Unknown key type, so we cannot parse it. */ 122 | free(der_key); 123 | return false; 124 | } 125 | 126 | /* fastHash mixes |nonce| with |message|. An attacker is assumed not to know 127 | the nonce value. When inserting messages into the cache, messages have 128 | verified cryptographic signatures. The attacker will not be able to control 129 | the bits in at least one 64-bit value of the message when inserting into the 130 | cache. When it is mixed with the random nonce, the attacker will not be able 131 | to predict the resulting hash. Four 64-bit lanes of data are hashed in 132 | parallel to take advantage of multiplier pipelining. The point of fastHash 133 | is to enable secure 64-bit cache entries that are resistant to offline 134 | attacks, rather than 128-bit entries that would be required without the 135 | nonce. An attacker attempting to pass the MessageAlreadyVerified check with 136 | a bogus message due to a collision has at most a 16 in 2^64 chance per 137 | on-line attempt, even if they can exactly control the resulting hash of bogus 138 | signatures. The entire justification for fastHash is speed. HMAC-SHA256 is 139 | ~1µs for this use case, while this function should hash at a rate close to 1 140 | byte/cycle, or about 40ns. */ 141 | static uint64_t fastHash(uint64_t nonce, const uint8_t* message, 142 | size_t message_len) { 143 | uint64_t hash[4] = {nonce, nonce, nonce, nonce}; 144 | uint64_t value[4] = {0, 0, 0, 0}; 145 | const uint8_t* p = message; 146 | size_t length = message_len; 147 | while (length != 0) { 148 | if (length >= 4 * sizeof(uint64_t)) { 149 | memcpy(&value, p, 4 * sizeof(uint64_t)); 150 | p += 4 * sizeof(uint64_t); 151 | length -= 4 * sizeof(uint64_t); 152 | } else { 153 | memcpy(&value, p, length); 154 | length = 0; 155 | } 156 | /* This loop will be unrolled, and the hash and value arrays assigned to 157 | registers for some pretty sweet speed. */ 158 | int i; 159 | for (i = 0; i < 4; i++) { 160 | /* Note that this is reversible, meaning hash[i] does not lose entropy. 161 | This is a Latin square from Lyra2's BlaMka. */ 162 | hash[i] += value[i] + 2 * hash[i] * value[i]; 163 | /* Ensure each in bit impacts at least 33 out bits. */ 164 | hash[i] ^= hash[i] >> 32; 165 | } 166 | } 167 | return hash[0] ^ hash[1] ^ hash[2] ^ hash[3]; 168 | } 169 | 170 | tbCache* tbCacheCreate(void) { 171 | /* If your code is failing this check, call LibInit first. */ 172 | if (tbCacheNonce == 0) { 173 | return NULL; 174 | } 175 | tbCache* cache = calloc(1, sizeof(tbCache)); 176 | if (cache == NULL) { 177 | return NULL; 178 | } 179 | cache->status = TB_CACHE_OK; 180 | return cache; 181 | } 182 | 183 | void tbCacheDestroy(tbCache* cache) { 184 | if (cache == NULL) { 185 | return; 186 | } 187 | if (cache->message_hashes != NULL) { 188 | free(cache->message_hashes); 189 | } 190 | free(cache); 191 | } 192 | 193 | void tbCacheLibInit(uint64_t nonce) { 194 | if (tbCacheNonce != 0) { 195 | /* The library only needs to be initilaized once. Changing the nonce would 196 | cause the cache to have garbage entries. */ 197 | return; 198 | } 199 | if (nonce == 0) { 200 | nonce = 1; 201 | } 202 | tbCacheNonce = nonce; 203 | } 204 | 205 | /* Lookup the hash in the small array of cached hashes and return the position 206 | if found. Otherwise, return -1. */ 207 | static int findHashInCache(tbCache* cache, uint64_t hash) { 208 | size_t i; 209 | for (i = 0; i < cache->num_message_hashes; i++) { 210 | if (cache->message_hashes[i] == hash) { 211 | return i; 212 | } 213 | } 214 | return -1; 215 | } 216 | 217 | /* Lookup the message in the message cache. Return true if it is there. */ 218 | static bool cacheLookup(tbCache* cache, const uint8_t* message, 219 | size_t message_len) { 220 | uint64_t hash = fastHash(tbCacheNonce, message, message_len); 221 | int pos = findHashInCache(cache, hash); 222 | if (pos == -1) { 223 | return false; 224 | } 225 | if (pos != 0) { 226 | /* Move hash to first position in LRU cache. */ 227 | memmove(cache->message_hashes + 1, cache->message_hashes, 228 | pos * sizeof(uint64_t)); 229 | cache->message_hashes[0] = hash; 230 | } 231 | return true; 232 | } 233 | 234 | /* Return true if x is a power of 2. Note: assumes x > 0. */ 235 | static bool isPowerOfTwo(uint32_t x) { 236 | return !(x & (x - 1)); 237 | } 238 | 239 | /* Add the hash of the message to the message hash cache. This sets the error 240 | code to TB_WARNING_CACHE_OVERFLOW if the cache overflowed. */ 241 | static void cacheAdd(tbCache* cache, const uint8_t* message, 242 | size_t message_len) { 243 | uint64_t hash = fastHash(tbCacheNonce, message, message_len); 244 | if (findHashInCache(cache, hash) != -1) { 245 | return; /* Already in cache */ 246 | } 247 | if (cache->num_message_hashes == 0) { 248 | cache->message_hashes = calloc(2, sizeof(uint64_t)); 249 | if (cache == NULL) { 250 | cache->status = TB_CACHE_MEMORY_ERROR; 251 | return; 252 | } 253 | } 254 | if (cache->num_message_hashes == TB_CACHE_SIZE) { 255 | memmove(cache->message_hashes + 1, cache->message_hashes, 256 | (TB_CACHE_SIZE - 1) * sizeof(uint64_t)); 257 | cache->status = TB_CACHE_OVERFLOW; 258 | } else { 259 | if (cache->num_message_hashes >= 2 && 260 | isPowerOfTwo(cache->num_message_hashes)) { 261 | uint64_t* message_hashes = 262 | realloc(cache->message_hashes, 263 | (cache->num_message_hashes << 1) * sizeof(uint64_t)); 264 | if (message_hashes == NULL) { 265 | cache->status = TB_CACHE_MEMORY_ERROR; 266 | return; 267 | } 268 | cache->message_hashes = message_hashes; 269 | } 270 | memmove(cache->message_hashes + 1, cache->message_hashes, 271 | cache->num_message_hashes * sizeof(uint64_t)); 272 | cache->num_message_hashes++; 273 | } 274 | cache->message_hashes[0] = hash; 275 | } 276 | 277 | bool tbCacheMessageAlreadyVerified(tbCache* cache, const uint8_t* message, 278 | size_t message_len, uint8_t** out_tokbind_id, 279 | size_t* out_tokbind_id_len, 280 | uint8_t** out_referred_tokbind_id, 281 | size_t* out_referred_tokbind_id_len) { 282 | if (out_tokbind_id == NULL || out_referred_tokbind_id == NULL) { 283 | return false; 284 | } 285 | *out_tokbind_id = NULL; 286 | *out_referred_tokbind_id = NULL; 287 | if (message_len == 0) { 288 | cache->status = TB_CACHE_INVALID_FORMAT; 289 | return false; 290 | } 291 | if (!cacheLookup(cache, message, message_len)) { 292 | cache->status = TB_CACHE_MISS; 293 | return false; 294 | } 295 | cache->status = TB_CACHE_HIT; 296 | tbCBS tokbind_message, message_contents; 297 | tbCBS_init(&tokbind_message, message, message_len); 298 | if (!tbCBS_get_u16_length_prefixed(&tokbind_message, &message_contents) || 299 | tbCBS_len(&message_contents) + 2 != message_len) { 300 | cache->status = TB_CACHE_INVALID_FORMAT; 301 | return false; 302 | } 303 | uint8_t* tokbind_id; 304 | size_t tokbind_id_len; 305 | uint8_t tokbind_type; 306 | tbKeyType type; 307 | while (tbCBS_len(&message_contents) != 0) { 308 | tbCBS signature; 309 | tbCBS extensions; // Currently we ignore all extensions 310 | if (!tbCBS_get_u8(&message_contents, &tokbind_type) || 311 | !extractTokenBindingID(&message_contents, &type, NULL, 312 | (const uint8_t**)&tokbind_id, &tokbind_id_len) || 313 | !tbCBS_get_u16_length_prefixed(&message_contents, &signature) || 314 | !tbCBS_get_u16_length_prefixed(&message_contents, &extensions)) { 315 | /* Should never happen, since message was already verified. */ 316 | cache->status = TB_CACHE_INVALID_FORMAT; 317 | return false; 318 | } 319 | if (tokbind_type == TB_PROVIDED) { 320 | *out_tokbind_id = tokbind_id; 321 | *out_tokbind_id_len = tokbind_id_len; 322 | } else if (tokbind_type == TB_REFERRED) { 323 | *out_referred_tokbind_id = tokbind_id; 324 | *out_referred_tokbind_id_len = tokbind_id_len; 325 | } else { 326 | /* Should never happen, since message was already verified. */ 327 | cache->status = TB_CACHE_INVALID_FORMAT; 328 | return false; 329 | } 330 | } 331 | return true; 332 | } 333 | 334 | bool tbCacheVerifyTokenBindingMessage( 335 | tbCache* cache, const uint8_t* message, size_t message_len, 336 | tbKeyType expected_key_type, const uint8_t ekm[TB_HASH_LEN], 337 | uint8_t** out_tokbind_id, size_t* out_tokbind_id_len, 338 | uint8_t** out_referred_tokbind_id, size_t* out_referred_tokbind_id_len) { 339 | if (message_len == 0 || out_tokbind_id == NULL || 340 | out_referred_tokbind_id == NULL) { 341 | cache->status = TB_CACHE_INVALID_FORMAT; 342 | return false; 343 | } 344 | *out_tokbind_id = NULL; 345 | *out_referred_tokbind_id = NULL; 346 | tbCBS tokbind_message, message_contents, signature; 347 | tbCBS_init(&tokbind_message, message, message_len); 348 | if (!tbCBS_get_u16_length_prefixed(&tokbind_message, &message_contents) || 349 | tbCBS_len(&message_contents) + 2 != message_len) { 350 | cache->status = TB_CACHE_INVALID_FORMAT; 351 | return false; 352 | } 353 | bool has_provided = false; 354 | bool has_referred = false; 355 | EVP_PKEY* evp_key = NULL; 356 | EVP_MD_CTX* md_ctx = NULL; 357 | uint8_t* dersig = NULL; 358 | size_t dersig_len; 359 | while (tbCBS_len(&message_contents) != 0) { 360 | uint8_t tokbind_type; 361 | tbKeyType key_type; 362 | /* Initialize variables we may need to free if an error occurs. */ 363 | evp_key = NULL; 364 | md_ctx = NULL; 365 | dersig = NULL; 366 | uint8_t* tokbind_id = NULL; 367 | size_t tokbind_id_len = 0; 368 | tbCBS extensions; // Currently we ignore all extensions 369 | if (!tbCBS_get_u8(&message_contents, &tokbind_type) || 370 | !extractTokenBindingID(&message_contents, &key_type, &evp_key, 371 | (const uint8_t**)&tokbind_id, &tokbind_id_len) || 372 | !tbCBS_get_u16_length_prefixed(&message_contents, &signature) || 373 | (tokbind_type == TB_PROVIDED && key_type != expected_key_type) || 374 | !tbCBS_get_u16_length_prefixed(&message_contents, &extensions)) { 375 | cache->status = TB_CACHE_INVALID_FORMAT; 376 | goto err; 377 | } 378 | const uint8_t* sig_data = tbCBS_data(&signature); 379 | size_t sig_data_len = tbCBS_len(&signature); 380 | /* Signature is in token binding format, but must be in DER format before 381 | passing to OpenSSL. */ 382 | if (!tbConvertTokenBindingSigToDerSig(sig_data, sig_data_len, key_type, 383 | &dersig, &dersig_len)) { 384 | cache->status = TB_CACHE_INVALID_FORMAT; 385 | goto err; 386 | } 387 | md_ctx = EVP_MD_CTX_create(); 388 | EVP_PKEY_CTX* key_ctx; 389 | uint8_t sigdata[TB_HASH_LEN + 2]; 390 | sigdata[0] = tokbind_type; 391 | sigdata[1] = key_type; 392 | memcpy(sigdata + 2, ekm, TB_HASH_LEN); 393 | if (EVP_DigestVerifyInit(md_ctx, &key_ctx, EVP_sha256(), NULL, evp_key) != 394 | 1 || 395 | !tbSetPadding(key_type, key_ctx) || 396 | !EVP_DigestVerifyUpdate(md_ctx, sigdata, TB_HASH_LEN + 2) || 397 | !EVP_DigestVerifyFinal(md_ctx, dersig, dersig_len)) { 398 | cache->status = TB_CACHE_BAD_SIGNATURE; 399 | goto err; 400 | } 401 | free(dersig); 402 | dersig = NULL; 403 | EVP_MD_CTX_destroy(md_ctx); 404 | md_ctx = NULL; 405 | EVP_PKEY_free(evp_key); 406 | evp_key = NULL; 407 | if (tokbind_type == TB_PROVIDED) { 408 | if (has_provided) { 409 | cache->status = TB_CACHE_INVALID_FORMAT; 410 | goto err; 411 | } 412 | has_provided = true; 413 | *out_tokbind_id = tokbind_id; 414 | *out_tokbind_id_len = tokbind_id_len; 415 | } else if (tokbind_type == TB_REFERRED) { 416 | if (has_referred) { 417 | cache->status = TB_CACHE_INVALID_FORMAT; 418 | goto err; 419 | } 420 | has_referred = true; 421 | *out_referred_tokbind_id = tokbind_id; 422 | *out_referred_tokbind_id_len = tokbind_id_len; 423 | } 424 | } 425 | if (!has_provided) { 426 | /* A provided token binding is required. */ 427 | cache->status = TB_CACHE_INVALID_FORMAT; 428 | goto err; 429 | } 430 | cache->status = TB_CACHE_GOOD_SIGNATURE; 431 | cacheAdd(cache, message, message_len); 432 | return true; 433 | err: 434 | if (evp_key != NULL) { 435 | EVP_PKEY_free(evp_key); 436 | } 437 | if (md_ctx != NULL) { 438 | EVP_MD_CTX_destroy(md_ctx); 439 | } 440 | if (dersig != NULL) { 441 | free(dersig); 442 | } 443 | return false; 444 | } 445 | 446 | const char* tbCacheGetStatusString(tbCacheStatus status) { 447 | switch (status) { 448 | case TB_CACHE_OK: 449 | return "cache-ok"; 450 | case TB_CACHE_BAD_SIGNATURE: 451 | return "bad-signature"; 452 | case TB_CACHE_GOOD_SIGNATURE: 453 | return "good-signature"; 454 | case TB_CACHE_HIT: 455 | return "cache-hit"; 456 | case TB_CACHE_INVALID_FORMAT: 457 | return "invalid-format"; 458 | case TB_CACHE_MEMORY_ERROR: 459 | return "memory-error"; 460 | case TB_CACHE_MISS: 461 | return "cache-miss"; 462 | case TB_CACHE_OVERFLOW: 463 | return "cache-overflow"; 464 | } 465 | return NULL; 466 | } 467 | 468 | EVP_PKEY* tbDecodeTokenBindingID(const uint8_t* tokbind_id, 469 | size_t tokbind_id_len, 470 | tbKeyType* out_key_type) { 471 | tbCBS tokbind_id_cbs; 472 | tbCBS_init(&tokbind_id_cbs, tokbind_id, tokbind_id_len); 473 | EVP_PKEY* evp_key; 474 | uint8_t* tb_id; 475 | size_t tb_id_len; 476 | if (!extractTokenBindingID(&tokbind_id_cbs, out_key_type, &evp_key, 477 | (const uint8_t**)&tb_id, &tb_id_len)) { 478 | return NULL; 479 | } 480 | if (tbCBS_len(&tokbind_id_cbs) != 0) { 481 | EVP_PKEY_free(evp_key); 482 | return NULL; 483 | } 484 | return evp_key; 485 | } 486 | -------------------------------------------------------------------------------- /token_bind_server.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | Author: waywardgeek@gmail.com (Bill Cox) 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 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | 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 and 14 | limitations under the License. */ 15 | 16 | #ifndef TOKEN_BIND_CSRC_TOKEN_BIND_SERVER_H_ 17 | #define TOKEN_BIND_CSRC_TOKEN_BIND_SERVER_H_ 18 | 19 | /* This library implements Token Binding, as specified in: 20 | https://datatracker.ietf.org/doc/draft-ietf-tokbind-protocol 21 | 22 | In essence, Token Binding is the next version of Channel ID. It is on track 23 | to become an IETF standard. */ 24 | 25 | #include 26 | #include "token_bind_common.h" 27 | 28 | /* tbCache should be allocated on a server's SSL connection when a Token Binding 29 | is negotiated successfully with the client. It provides APIs for validating 30 | Token Binding headers, as well as keeping a small cache of previously 31 | validated headers, to reduce the work required per HTTP request. */ 32 | struct tbCacheStruct; 33 | typedef struct tbCacheStruct tbCache; 34 | 35 | #define TB_CACHE_SIZE 16 36 | 37 | tbCache* tbCacheCreate(void); 38 | 39 | void tbCacheDestroy(tbCache* cache); 40 | 41 | /* tbCacheLibInit must be called once before using the Token Binding library to 42 | verify Token Binding headers. The nonce value will be used to randomize 43 | hashing in the message cache, defending against certain collision attacks. 44 | If used in a client that does not verify Token Binding headers, this function 45 | does not need to be called. |nonce| must not be 0, which generally is safe 46 | to assume when using a solid true random number generator. */ 47 | void tbCacheLibInit(uint64_t nonce); 48 | 49 | /* All the tbCache methods set a status code on the cache object. */ 50 | typedef enum { 51 | TB_CACHE_OK, 52 | TB_CACHE_BAD_SIGNATURE, 53 | TB_CACHE_GOOD_SIGNATURE, 54 | TB_CACHE_HIT, 55 | TB_CACHE_INVALID_FORMAT, 56 | TB_CACHE_MEMORY_ERROR, 57 | TB_CACHE_MISS, 58 | TB_CACHE_OVERFLOW, 59 | } tbCacheStatus; 60 | 61 | /* tbCacheGetStatus returns the status code set by the last method call. */ 62 | tbCacheStatus tbCacheGetStatus(tbCache* cache); 63 | 64 | /* tbCacheMessageAlreadyVerified checks to see if the message is in the cache of 65 | messages that have already been verified using this tbCache. This must be 66 | called before calling tbCacheVerifyTokenBindingMessage. False is returned if 67 | |message| is not in the cache. No checks for validity are performed. If the 68 | message has been verified before, |tokbind_id| is set to the portion of 69 | |message| representing the public key (the TokenBindingID). If there is a 70 | referred token binding, then referred_pubkey is set as well. This function 71 | is separate from tbCacheVerifyTokenBindingMessage so that the caller will not 72 | need to generate the EKM value if we have already verified the message. The 73 | output parameters may not be NULL. */ 74 | bool tbCacheMessageAlreadyVerified(tbCache* cache, const uint8_t* message, 75 | size_t message_len, uint8_t** out_tokbind_id, 76 | size_t* out_tokbind_id_len, 77 | uint8_t** out_referred_tokbind_id, 78 | size_t* out_referred_tokbind_id_len); 79 | 80 | /* tbCacheVerifyTokenBindingMessage parses a token binding message in |message|, 81 | and verifies that it contains a valid signature of |ekm|. It can be compute 82 | intensive, so tbCacheMessageAlreadyVerified must be called first to see if 83 | verification is required. False is returned if |message| cannot be parsed, 84 | if the signature is invalid, or if the key type in the Token Binding Message 85 | does not match |expected_key_type|. The key type should be determined from 86 | the TLS negotiation. On success, |tokbind_id| is set to the portion of 87 | |message| representing the public key (the TokenBindingID). The token 88 | binding message can optionally contain a "referred" token binding, which is 89 | used in federated scenarios. The key type for a referred token binding can 90 | be different than |expected_key_type|, so use 91 | tbGetKeyType(referred_tokbind_id) to get the referred key type. 92 | |referred_tokbind_id| will be set to the referred public key if present in the 93 | token binding message, or NULL if not. The output parameters may not be 94 | NULL. */ 95 | bool tbCacheVerifyTokenBindingMessage( 96 | tbCache* cache, const uint8_t* message, size_t message_len, 97 | tbKeyType expected_key_type, const uint8_t ekm[TB_HASH_LEN], 98 | uint8_t** out_tokbind_id, size_t* out_tokbind_id_len, 99 | uint8_t** out_referred_tokbind_id, size_t* out_referred_tokbind_id_len); 100 | 101 | /* GetStatusString returns a web-safe string that describes the cache status. 102 | The string can be written to logs or appended to headers for debugging. */ 103 | const char* tbCacheGetStatusString(tbCacheStatus status); 104 | 105 | /* tbDecodeTokenBindingID converts a key string in token Binding format 106 | (TokenBindingID) to a Token Binding compatible EVP_PKEY, and sets 107 | |out_key_type| to the Token Binding type of the key. If |key| is not a valid 108 | Token Binding key, NULL is returned. Call tbEncodeKey to convert an EVP_PKEY 109 | object to Token Binding format. The caller takes ownership of the returned 110 | EVP_PKEY. */ 111 | EVP_PKEY* tbDecodeTokenBindingID(const uint8_t* tokbind_id, 112 | size_t tokbind_id_len, 113 | tbKeyType* out_key_type); 114 | 115 | #endif /* TOKEN_BIND_TOKEN_BIND_SERVER_H_ */ 116 | --------------------------------------------------------------------------------