roughtime.googlesource.com/roughtime.git@v0.0.0-20201210012726-dd529367052d/client.cc (about) 1 /* Copyright 2016 The Roughtime Authors. 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 #include <string> 16 17 #include <stdint.h> 18 19 #include <openssl/curve25519.h> 20 21 #include "client.h" 22 #include "logging.h" 23 24 namespace roughtime { 25 26 std::string CreateRequest(const uint8_t nonce[kNonceLength]) { 27 uint8_t query_bytes[kMinRequestSize]; 28 size_t query_len; 29 30 Builder query(query_bytes, sizeof(query_bytes), 2); 31 32 uint8_t* padding; 33 34 static_assert(kTagNONC < kTagPAD, "Tags must be written in order"); 35 ROUGHTIME_CHECK(query.AddTagData(kTagNONC, nonce, kNonceLength) && 36 query.AddTag(&padding, kTagPAD, kPaddingLen) && 37 query.Finish(&query_len)); 38 ROUGHTIME_CHECK_EQ(query_len, sizeof(query_bytes)); 39 40 memset(padding, 0, kPaddingLen); 41 42 return std::string(reinterpret_cast<char*>(query_bytes), query_len); 43 } 44 45 bool ParseResponse(rough_time_t* out_time, uint32_t* out_radius, 46 std::string* out_error, 47 const uint8_t root_public_key[ED25519_PUBLIC_KEY_LEN], 48 const uint8_t* response_bytes, size_t response_len, 49 const uint8_t nonce[kNonceLength]) { 50 *out_time = 0; 51 *out_radius = 0; 52 53 Parser response(response_bytes, response_len); 54 if (!response.is_valid()) { 55 *out_error = "structural error"; 56 return false; 57 } 58 59 const uint8_t* cert_bytes; 60 size_t cert_len; 61 if (!response.GetTag(&cert_bytes, &cert_len, kTagCERT)) { 62 *out_error = "no certificate provided"; 63 return false; 64 } 65 66 Parser cert(cert_bytes, cert_len); 67 if (!cert.is_valid()) { 68 *out_error = "structural error in certificate"; 69 return false; 70 } 71 72 const uint8_t* signature; 73 if (!cert.GetFixedLen(&signature, kTagSIG, ED25519_SIGNATURE_LEN)) { 74 *out_error = "no signature in certificate"; 75 return false; 76 } 77 78 const uint8_t* delegation_bytes; 79 size_t delegation_len; 80 if (!cert.GetTag(&delegation_bytes, &delegation_len, kTagDELE)) { 81 *out_error = "no delegation in certificate"; 82 return false; 83 } 84 85 std::string signed_message = 86 std::string(kCertContextString, sizeof(kCertContextString)) + 87 std::string(reinterpret_cast<const char*>(delegation_bytes), 88 delegation_len); 89 90 if (!ED25519_verify(reinterpret_cast<const uint8_t*>(signed_message.data()), 91 signed_message.size(), signature, root_public_key)) { 92 *out_error = "bad signature in certificate"; 93 return false; 94 } 95 96 const uint8_t* delegated_public_key; 97 rough_time_t min_time, max_time; 98 Parser delegation(delegation_bytes, delegation_len); 99 if (!delegation.is_valid() || 100 !delegation.Get(&min_time, kTagMINT) || 101 !delegation.Get(&max_time, kTagMAXT) || 102 !delegation.GetFixedLen(&delegated_public_key, kTagPUBK, 103 ED25519_PUBLIC_KEY_LEN)) { 104 *out_error = "delegation missing required value"; 105 return false; 106 } 107 108 if (max_time < min_time) { 109 *out_error = "invalid delegation validity period"; 110 return false; 111 } 112 113 const uint8_t* signed_response_bytes; 114 size_t signed_response_len; 115 if (!response.GetTag(&signed_response_bytes, &signed_response_len, 116 kTagSREP)) { 117 *out_error = "no signed response"; 118 return false; 119 } 120 121 const uint8_t* response_signature; 122 if (!response.GetFixedLen(&response_signature, kTagSIG, 123 ED25519_SIGNATURE_LEN)) { 124 *out_error = "no signature in response"; 125 return false; 126 } 127 128 signed_message = 129 std::string(kContextString, sizeof(kContextString)) + 130 std::string(reinterpret_cast<const char*>(signed_response_bytes), 131 signed_response_len); 132 133 if (!ED25519_verify(reinterpret_cast<const uint8_t*>(signed_message.data()), 134 signed_message.size(), response_signature, 135 delegated_public_key)) { 136 *out_error = "bad signature in response"; 137 return false; 138 } 139 140 Parser signed_response(signed_response_bytes, signed_response_len); 141 if (!signed_response.is_valid()) { 142 *out_error = "invalid signed response in response"; 143 return false; 144 } 145 146 const uint8_t* root; 147 rough_time_t timestamp; 148 uint32_t radius; 149 if (!signed_response.GetFixedLen(&root, kTagROOT, kNonceLength) || 150 !signed_response.Get(×tamp, kTagMIDP) || 151 !signed_response.Get(&radius, kTagRADI)) { 152 *out_error = "signed response missing required values"; 153 return false; 154 } 155 156 if (timestamp < min_time || max_time < timestamp) { 157 *out_error = "timestamp out of range for delegation"; 158 return false; 159 } 160 161 const uint8_t* path; 162 size_t path_len; 163 uint32_t tree_index; 164 if (!response.Get(&tree_index, kTagINDX) || 165 !response.GetTag(&path, &path_len, kTagPATH)) { 166 *out_error = "response missing required values"; 167 return false; 168 } 169 170 uint8_t hash[kNonceLength]; 171 HashLeaf(hash, nonce); 172 173 if (path_len % kNonceLength != 0) { 174 *out_error = "tree path is not a multiple of the hash size"; 175 return false; 176 } 177 178 for (size_t i = 0; i < path_len; i += kNonceLength) { 179 const bool path_element_is_right = tree_index & 1; 180 if (path_element_is_right) { 181 HashNode(hash, hash, path + i); 182 } else { 183 HashNode(hash, path + i, hash); 184 } 185 tree_index /= 2; 186 } 187 188 if (memcmp(root, hash, kNonceLength) != 0) { 189 *out_error = "calculated tree root doesn't match signed root"; 190 return false; 191 } 192 193 *out_time = timestamp; 194 *out_radius = radius; 195 196 return true; 197 } 198 199 } // namespace roughtime