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(&timestamp, 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