| // Copyright 2015 The Weave Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "src/macaroon_caveat.h" | 
 | #include "src/macaroon_caveat_internal.h" | 
 |  | 
 | #include <string.h> | 
 |  | 
 | #include "src/crypto_hmac.h" | 
 | #include "src/macaroon.h" | 
 | #include "src/macaroon_context.h" | 
 | #include "src/macaroon_encoding.h" | 
 |  | 
 | static bool is_valid_caveat_type_(UwMacaroonCaveatType type) { | 
 |   switch (type) { | 
 |     case kUwMacaroonCaveatTypeNonce: | 
 |     case kUwMacaroonCaveatTypeScope: | 
 |     case kUwMacaroonCaveatTypeExpirationAbsolute: | 
 |     case kUwMacaroonCaveatTypeTTL1Hour: | 
 |     case kUwMacaroonCaveatTypeTTL24Hour: | 
 |     case kUwMacaroonCaveatTypeDelegationTimestamp: | 
 |     case kUwMacaroonCaveatTypeDelegateeUser: | 
 |     case kUwMacaroonCaveatTypeDelegateeApp: | 
 |     case kUwMacaroonCaveatTypeAppCommandsOnly: | 
 |     case kUwMacaroonCaveatTypeDelegateeService: | 
 |     case kUwMacaroonCaveatTypeBleSessionID: | 
 |     case kUwMacaroonCaveatTypeLanSessionID: | 
 |     case kUwMacaroonCaveatTypeAuthenticationChallenge: | 
 |     case kUwMacaroonCaveatTypeClientAuthorizationTokenV1: | 
 |     case kUwMacaroonCaveatTypeServerAuthenticationTokenV1: | 
 |       return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | static bool is_valid_scope_type_(UwMacaroonCaveatScopeType type) { | 
 |   switch (type) { | 
 |     case kUwMacaroonCaveatScopeTypeOwner: | 
 |     case kUwMacaroonCaveatScopeTypeManager: | 
 |     case kUwMacaroonCaveatScopeTypeUser: | 
 |     case kUwMacaroonCaveatScopeTypeViewer: | 
 |       return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | static bool is_valid_service_id_(UwMacaroonCaveatCloudServiceId service_id) { | 
 |   switch (service_id) { | 
 |     case kUwMacaroonCaveatCloudServiceIdNotCloudRegistered: | 
 |     case kUwMacaroonCaveatCloudServiceIdGoogleWeave: | 
 |       return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | static bool create_caveat_no_value_(UwMacaroonCaveatType type, | 
 |                                     uint8_t* buffer, | 
 |                                     size_t buffer_size, | 
 |                                     UwMacaroonCaveat* new_caveat) { | 
 |   // (buffer_size == 0 || get_buffsize_() > buffer_size) will conver the case | 
 |   // that get_buffer_size_() returns 0 (for errors), so there is no need to | 
 |   // check get_buffer_size_() == 0 again. | 
 |   if (buffer == NULL || buffer_size == 0 || new_caveat == NULL || | 
 |       uw_macaroon_caveat_creation_get_buffsize_(type, 0) > buffer_size) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   size_t encoded_str_len = 0, total_str_len = 0; | 
 |   if (!uw_macaroon_encoding_encode_uint_((uint32_t)type, buffer, buffer_size, | 
 |                                          &encoded_str_len)) { | 
 |     return false; | 
 |   } | 
 |   total_str_len += encoded_str_len; | 
 |  | 
 |   new_caveat->bytes = buffer; | 
 |   new_caveat->num_bytes = total_str_len; | 
 |   return true; | 
 | } | 
 |  | 
 | static bool create_caveat_uint_value_(UwMacaroonCaveatType type, | 
 |                                       uint32_t unsigned_int, | 
 |                                       uint8_t* buffer, | 
 |                                       size_t buffer_size, | 
 |                                       UwMacaroonCaveat* new_caveat) { | 
 |   if (buffer == NULL || buffer_size == 0 || new_caveat == NULL || | 
 |       uw_macaroon_caveat_creation_get_buffsize_(type, 0) > buffer_size) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   size_t encoded_str_len = 0, total_str_len = 0; | 
 |   if (!uw_macaroon_encoding_encode_uint_((uint32_t)type, buffer, buffer_size, | 
 |                                          &encoded_str_len)) { | 
 |     return false; | 
 |   } | 
 |   total_str_len += encoded_str_len; | 
 |   if (!uw_macaroon_encoding_encode_uint_(unsigned_int, buffer + total_str_len, | 
 |                                          buffer_size - total_str_len, | 
 |                                          &encoded_str_len)) { | 
 |     return false; | 
 |   } | 
 |   total_str_len += encoded_str_len; | 
 |  | 
 |   new_caveat->bytes = buffer; | 
 |   new_caveat->num_bytes = total_str_len; | 
 |   return true; | 
 | } | 
 |  | 
 | static bool create_caveat_bstr_value_(UwMacaroonCaveatType type, | 
 |                                       const uint8_t* str, | 
 |                                       size_t str_len, | 
 |                                       uint8_t* buffer, | 
 |                                       size_t buffer_size, | 
 |                                       UwMacaroonCaveat* new_caveat) { | 
 |   if ((str == NULL && str_len != 0) || buffer == NULL || buffer_size == 0 || | 
 |       new_caveat == NULL || | 
 |       uw_macaroon_caveat_creation_get_buffsize_(type, str_len) > buffer_size) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   size_t encoded_str_len = 0, total_str_len = 0; | 
 |   if (!uw_macaroon_encoding_encode_uint_((uint32_t)type, buffer, buffer_size, | 
 |                                          &encoded_str_len)) { | 
 |     return false; | 
 |   } | 
 |   total_str_len += encoded_str_len; | 
 |   if (!uw_macaroon_encoding_encode_byte_str_( | 
 |           str, str_len, buffer + total_str_len, buffer_size - total_str_len, | 
 |           &encoded_str_len)) { | 
 |     return false; | 
 |   } | 
 |   total_str_len += encoded_str_len; | 
 |  | 
 |   new_caveat->bytes = buffer; | 
 |   new_caveat->num_bytes = total_str_len; | 
 |   return true; | 
 | } | 
 |  | 
 | size_t uw_macaroon_caveat_creation_get_buffsize_(UwMacaroonCaveatType type, | 
 |                                                  size_t str_len) { | 
 |   switch (type) { | 
 |     // No values | 
 |     case kUwMacaroonCaveatTypeTTL1Hour: | 
 |     case kUwMacaroonCaveatTypeTTL24Hour: | 
 |     case kUwMacaroonCaveatTypeAppCommandsOnly: | 
 |     case kUwMacaroonCaveatTypeBleSessionID: | 
 |     case kUwMacaroonCaveatTypeAuthenticationChallenge: | 
 |       return UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN; | 
 |  | 
 |     // Unsigned integers | 
 |     case kUwMacaroonCaveatTypeScope: | 
 |     case kUwMacaroonCaveatTypeDelegateeService: | 
 |     case kUwMacaroonCaveatTypeExpirationAbsolute: | 
 |     case kUwMacaroonCaveatTypeDelegationTimestamp: | 
 |       return 2 * UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN; | 
 |  | 
 |     // Byte strings | 
 |     case kUwMacaroonCaveatTypeNonce: | 
 |     case kUwMacaroonCaveatTypeDelegateeUser: | 
 |     case kUwMacaroonCaveatTypeDelegateeApp: | 
 |     case kUwMacaroonCaveatTypeLanSessionID: | 
 |     case kUwMacaroonCaveatTypeClientAuthorizationTokenV1: | 
 |     case kUwMacaroonCaveatTypeServerAuthenticationTokenV1: | 
 |       return str_len + 2 * UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN; | 
 |  | 
 |     default: | 
 |       return 0;  // For errors | 
 |   } | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_create_nonce_(const uint8_t* nonce, | 
 |                                       size_t nonce_size, | 
 |                                       uint8_t* buffer, | 
 |                                       size_t buffer_size, | 
 |                                       UwMacaroonCaveat* new_caveat) { | 
 |   return create_caveat_bstr_value_(kUwMacaroonCaveatTypeNonce, nonce, | 
 |                                    nonce_size, buffer, buffer_size, new_caveat); | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_create_scope_(UwMacaroonCaveatScopeType scope, | 
 |                                       uint8_t* buffer, | 
 |                                       size_t buffer_size, | 
 |                                       UwMacaroonCaveat* new_caveat) { | 
 |   if (!is_valid_scope_type_(scope)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   return create_caveat_uint_value_(kUwMacaroonCaveatTypeScope, scope, buffer, | 
 |                                    buffer_size, new_caveat); | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_create_expiration_absolute_( | 
 |     uint32_t expiration_time, | 
 |     uint8_t* buffer, | 
 |     size_t buffer_size, | 
 |     UwMacaroonCaveat* new_caveat) { | 
 |   return create_caveat_uint_value_(kUwMacaroonCaveatTypeExpirationAbsolute, | 
 |                                    expiration_time, buffer, buffer_size, | 
 |                                    new_caveat); | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_create_ttl_1_hour_(uint8_t* buffer, | 
 |                                            size_t buffer_size, | 
 |                                            UwMacaroonCaveat* new_caveat) { | 
 |   return create_caveat_no_value_(kUwMacaroonCaveatTypeTTL1Hour, buffer, | 
 |                                  buffer_size, new_caveat); | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_create_ttl_24_hour_(uint8_t* buffer, | 
 |                                             size_t buffer_size, | 
 |                                             UwMacaroonCaveat* new_caveat) { | 
 |   return create_caveat_no_value_(kUwMacaroonCaveatTypeTTL24Hour, buffer, | 
 |                                  buffer_size, new_caveat); | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_create_delegation_timestamp_( | 
 |     uint32_t timestamp, | 
 |     uint8_t* buffer, | 
 |     size_t buffer_size, | 
 |     UwMacaroonCaveat* new_caveat) { | 
 |   return create_caveat_uint_value_(kUwMacaroonCaveatTypeDelegationTimestamp, | 
 |                                    timestamp, buffer, buffer_size, new_caveat); | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_create_delegatee_user_(const uint8_t* id_str, | 
 |                                                size_t id_str_len, | 
 |                                                uint8_t* buffer, | 
 |                                                size_t buffer_size, | 
 |                                                UwMacaroonCaveat* new_caveat) { | 
 |   return create_caveat_bstr_value_(kUwMacaroonCaveatTypeDelegateeUser, id_str, | 
 |                                    id_str_len, buffer, buffer_size, new_caveat); | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_create_delegatee_app_(const uint8_t* id_str, | 
 |                                               size_t id_str_len, | 
 |                                               uint8_t* buffer, | 
 |                                               size_t buffer_size, | 
 |                                               UwMacaroonCaveat* new_caveat) { | 
 |   return create_caveat_bstr_value_(kUwMacaroonCaveatTypeDelegateeApp, id_str, | 
 |                                    id_str_len, buffer, buffer_size, new_caveat); | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_create_app_commands_only_( | 
 |     uint8_t* buffer, | 
 |     size_t buffer_size, | 
 |     UwMacaroonCaveat* new_caveat) { | 
 |   return create_caveat_no_value_(kUwMacaroonCaveatTypeAppCommandsOnly, buffer, | 
 |                                  buffer_size, new_caveat); | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_create_delegatee_service_( | 
 |     UwMacaroonCaveatCloudServiceId service_id, | 
 |     uint8_t* buffer, | 
 |     size_t buffer_size, | 
 |     UwMacaroonCaveat* new_caveat) { | 
 |   if (!is_valid_service_id_(service_id)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   return create_caveat_uint_value_(kUwMacaroonCaveatTypeDelegateeService, | 
 |                                    (uint32_t)service_id, buffer, buffer_size, | 
 |                                    new_caveat); | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_create_ble_session_id_(uint8_t* buffer, | 
 |                                                size_t buffer_size, | 
 |                                                UwMacaroonCaveat* new_caveat) { | 
 |   return create_caveat_no_value_(kUwMacaroonCaveatTypeBleSessionID, buffer, | 
 |                                  buffer_size, new_caveat); | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_create_lan_session_id_(const uint8_t* session_id, | 
 |                                                size_t session_id_len, | 
 |                                                uint8_t* buffer, | 
 |                                                size_t buffer_size, | 
 |                                                UwMacaroonCaveat* new_caveat) { | 
 |   return create_caveat_bstr_value_(kUwMacaroonCaveatTypeLanSessionID, | 
 |                                    session_id, session_id_len, buffer, | 
 |                                    buffer_size, new_caveat); | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_create_authentication_challenge_( | 
 |     uint8_t* buffer, | 
 |     size_t buffer_size, | 
 |     UwMacaroonCaveat* new_caveat) { | 
 |   return create_caveat_no_value_(kUwMacaroonCaveatTypeAuthenticationChallenge, | 
 |                                  buffer, buffer_size, new_caveat); | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_create_client_authorization_token_( | 
 |     const uint8_t* str, | 
 |     size_t str_len, | 
 |     uint8_t* buffer, | 
 |     size_t buffer_size, | 
 |     UwMacaroonCaveat* new_caveat) { | 
 |   if (str_len == 0) { | 
 |     return create_caveat_no_value_( | 
 |         kUwMacaroonCaveatTypeClientAuthorizationTokenV1, buffer, buffer_size, | 
 |         new_caveat); | 
 |   } | 
 |   return create_caveat_bstr_value_( | 
 |       kUwMacaroonCaveatTypeClientAuthorizationTokenV1, str, str_len, buffer, | 
 |       buffer_size, new_caveat); | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_create_server_authentication_token_( | 
 |     const uint8_t* str, | 
 |     size_t str_len, | 
 |     uint8_t* buffer, | 
 |     size_t buffer_size, | 
 |     UwMacaroonCaveat* new_caveat) { | 
 |   if (str_len == 0) { | 
 |     return create_caveat_no_value_( | 
 |         kUwMacaroonCaveatTypeServerAuthenticationTokenV1, buffer, buffer_size, | 
 |         new_caveat); | 
 |   } | 
 |   return create_caveat_bstr_value_( | 
 |       kUwMacaroonCaveatTypeServerAuthenticationTokenV1, str, str_len, buffer, | 
 |       buffer_size, new_caveat); | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_get_type_(const UwMacaroonCaveat* caveat, | 
 |                                   UwMacaroonCaveatType* type) { | 
 |   if (caveat == NULL || type == NULL) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   uint32_t unsigned_int; | 
 |   if (!uw_macaroon_encoding_decode_uint_(caveat->bytes, caveat->num_bytes, | 
 |                                          &unsigned_int)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   *type = (UwMacaroonCaveatType)unsigned_int; | 
 |   return is_valid_caveat_type_(*type); | 
 | } | 
 |  | 
 | /* === Some internal functions defined in macaroon_caveat_internal.h === */ | 
 |  | 
 | bool uw_macaroon_caveat_sign_(const uint8_t* key, | 
 |                               size_t key_len, | 
 |                               const UwMacaroonContext* context, | 
 |                               const UwMacaroonCaveat* caveat, | 
 |                               uint8_t* mac_tag, | 
 |                               size_t mac_tag_size) { | 
 |   if (key == NULL || key_len == 0 || context == NULL || caveat == NULL || | 
 |       mac_tag == NULL || mac_tag_size == 0) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   UwMacaroonCaveatType caveat_type; | 
 |   if (!uw_macaroon_caveat_get_type_(caveat, &caveat_type)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   // Need to encode the whole caveat as a byte string and then sign it | 
 |  | 
 |   // If there is no additional value from the context, just compute the HMAC on | 
 |   // the current byte string. | 
 |   uint8_t caveat_cbor_prefix[UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN] = {0}; | 
 |   size_t caveat_cbor_prefix_len = 0; | 
 |   if (caveat_type != kUwMacaroonCaveatTypeBleSessionID && | 
 |       caveat_type != kUwMacaroonCaveatTypeAuthenticationChallenge) { | 
 |     if (!uw_macaroon_encoding_encode_byte_str_len_( | 
 |             (uint32_t)(caveat->num_bytes), caveat_cbor_prefix, | 
 |             sizeof(caveat_cbor_prefix), &caveat_cbor_prefix_len)) { | 
 |       return false; | 
 |     } | 
 |  | 
 |     UwCryptoHmacMsg messages[] = { | 
 |         {caveat_cbor_prefix, caveat_cbor_prefix_len}, | 
 |         {caveat->bytes, caveat->num_bytes}, | 
 |     }; | 
 |  | 
 |     return uw_crypto_hmac_(key, key_len, messages, | 
 |                            sizeof(messages) / sizeof(messages[0]), mac_tag, | 
 |                            mac_tag_size); | 
 |   } | 
 |  | 
 |   // If there is additional value from the context. | 
 |   const uint8_t* additional_value_str = NULL; | 
 |   size_t additional_value_str_len = 0; | 
 |   if (caveat_type == kUwMacaroonCaveatTypeBleSessionID) { | 
 |     if (context->ble_session_id == NULL || context->ble_session_id_len == 0) { | 
 |       return false; | 
 |     } | 
 |  | 
 |     additional_value_str = context->ble_session_id; | 
 |     additional_value_str_len = context->ble_session_id_len; | 
 |   } else {  // kUwMacaroonCaveatTypeAuthenticationChallenge | 
 |     if (context->auth_challenge_str == NULL || | 
 |         context->auth_challenge_str_len == 0) { | 
 |       return false; | 
 |     } | 
 |  | 
 |     additional_value_str = context->auth_challenge_str; | 
 |     additional_value_str_len = context->auth_challenge_str_len; | 
 |   } | 
 |  | 
 |   uint8_t value_cbor_prefix[UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN] = {0}; | 
 |   size_t value_cbor_prefix_len = 0; | 
 |   if (!uw_macaroon_encoding_encode_byte_str_len_( | 
 |           (uint32_t)additional_value_str_len, value_cbor_prefix, | 
 |           sizeof(value_cbor_prefix), &value_cbor_prefix_len)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   // The length here includes: 1. the header for the whole byte string; 2. the | 
 |   // header for the additional value part; 3. the additional value part. | 
 |   size_t total_length = | 
 |       caveat->num_bytes + value_cbor_prefix_len + additional_value_str_len; | 
 |   if (!uw_macaroon_encoding_encode_byte_str_len_( | 
 |           (uint32_t)total_length, caveat_cbor_prefix, | 
 |           sizeof(caveat_cbor_prefix), &caveat_cbor_prefix_len)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   UwCryptoHmacMsg messages[] = { | 
 |       {caveat_cbor_prefix, caveat_cbor_prefix_len}, | 
 |       {caveat->bytes, caveat->num_bytes}, | 
 |       {value_cbor_prefix, value_cbor_prefix_len}, | 
 |       {additional_value_str, additional_value_str_len}, | 
 |   }; | 
 |  | 
 |   return uw_crypto_hmac_(key, key_len, messages, | 
 |                          sizeof(messages) / sizeof(messages[0]), mac_tag, | 
 |                          mac_tag_size); | 
 | } | 
 |  | 
 | static bool update_and_check_expiration_time( | 
 |     uint32_t current_time, | 
 |     uint32_t new_expiration_time, | 
 |     UwMacaroonValidationResult* result) { | 
 |   if (result->expiration_time > new_expiration_time) { | 
 |     result->expiration_time = new_expiration_time; | 
 |   } | 
 |  | 
 |   return current_time <= result->expiration_time; | 
 | } | 
 |  | 
 | static bool update_delegatee_list(UwMacaroonCaveatType caveat_type, | 
 |                                   const UwMacaroonCaveat* caveat, | 
 |                                   uint32_t issued_time, | 
 |                                   UwMacaroonValidationResult* result) { | 
 |   if (result->num_delegatees >= MAX_NUM_DELEGATEES || issued_time == 0) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   UwMacaroonDelegateeType delegatee_type = kUwMacaroonDelegateeTypeNone; | 
 |   switch (caveat_type) { | 
 |     case kUwMacaroonCaveatTypeDelegateeUser: | 
 |       delegatee_type = kUwMacaroonDelegateeTypeUser; | 
 |       break; | 
 |  | 
 |     case kUwMacaroonCaveatTypeDelegateeApp: | 
 |       delegatee_type = kUwMacaroonDelegateeTypeApp; | 
 |       break; | 
 |  | 
 |     case kUwMacaroonCaveatTypeDelegateeService: | 
 |       delegatee_type = kUwMacaroonDelegateeTypeService; | 
 |       break; | 
 |  | 
 |     default: | 
 |       return false; | 
 |   } | 
 |  | 
 |   if (caveat_type != kUwMacaroonCaveatTypeDelegateeUser) { | 
 |     for (size_t i = 0; i < result->num_delegatees; i++) { | 
 |       // There must have at most one DelegateeApp or DelegateeService | 
 |       if (result->delegatees[i].type == delegatee_type) { | 
 |         return false; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   if (caveat_type != kUwMacaroonCaveatTypeDelegateeService) { | 
 |     if (!uw_macaroon_caveat_get_value_bstr_( | 
 |             caveat, &(result->delegatees[result->num_delegatees].id), | 
 |             &(result->delegatees[result->num_delegatees].id_len))) { | 
 |       return false; | 
 |     } | 
 |     result->delegatees[result->num_delegatees].service_id = | 
 |         kUwMacaroonCaveatCloudServiceIdNotCloudRegistered;  // Default value | 
 |  | 
 |   } else { | 
 |     uint32_t service_id = 0; | 
 |     if (!uw_macaroon_caveat_get_value_uint_(caveat, &service_id)) { | 
 |       return false; | 
 |     } | 
 |     if (!is_valid_service_id_((UwMacaroonCaveatCloudServiceId)service_id)) { | 
 |       return false; | 
 |     } | 
 |     result->delegatees[result->num_delegatees].service_id = | 
 |         (UwMacaroonCaveatCloudServiceId)service_id; | 
 |     result->delegatees[result->num_delegatees].id = NULL; | 
 |     result->delegatees[result->num_delegatees].id_len = 0; | 
 |   } | 
 |  | 
 |   result->delegatees[result->num_delegatees].type = delegatee_type; | 
 |   result->delegatees[result->num_delegatees].timestamp = issued_time; | 
 |   result->num_delegatees++; | 
 |   return true; | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_validate_(const UwMacaroonCaveat* caveat, | 
 |                                   const UwMacaroonContext* context, | 
 |                                   UwMacaroonValidationState* state, | 
 |                                   UwMacaroonValidationResult* result) { | 
 |   if (caveat == NULL || context == NULL || state == NULL || result == NULL) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   uint32_t expiration_time = 0; | 
 |   uint32_t issued_time = 0; | 
 |   uint32_t scope = UW_MACAROON_CAVEAT_SCOPE_LOWEST_POSSIBLE; | 
 |  | 
 |   UwMacaroonCaveatType caveat_type; | 
 |   if (!uw_macaroon_caveat_get_type_(caveat, &caveat_type)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   switch (caveat_type) { | 
 |     // The types that always validate | 
 |     case kUwMacaroonCaveatTypeClientAuthorizationTokenV1: | 
 |     case kUwMacaroonCaveatTypeServerAuthenticationTokenV1: | 
 |     case kUwMacaroonCaveatTypeNonce: | 
 |     case kUwMacaroonCaveatTypeBleSessionID: | 
 |     case kUwMacaroonCaveatTypeAuthenticationChallenge: | 
 |       return true; | 
 |  | 
 |     case kUwMacaroonCaveatTypeDelegationTimestamp: | 
 |       if (!uw_macaroon_caveat_get_value_uint_(caveat, &issued_time) || | 
 |           issued_time < state->issued_time) { | 
 |         return false; | 
 |       } | 
 |       state->issued_time = issued_time; | 
 |       return true; | 
 |  | 
 |     case kUwMacaroonCaveatTypeTTL1Hour: | 
 |       if (state->issued_time == 0) { | 
 |         return false; | 
 |       } | 
 |       return update_and_check_expiration_time( | 
 |           context->current_time, state->issued_time + 60 * 60, result); | 
 |  | 
 |     case kUwMacaroonCaveatTypeTTL24Hour: | 
 |       if (state->issued_time == 0) { | 
 |         return false; | 
 |       } | 
 |       return update_and_check_expiration_time( | 
 |           context->current_time, state->issued_time + 24 * 60 * 60, result); | 
 |  | 
 |     // Need to create a list of delegatees | 
 |     case kUwMacaroonCaveatTypeDelegateeUser: | 
 |     case kUwMacaroonCaveatTypeDelegateeApp: | 
 |     case kUwMacaroonCaveatTypeDelegateeService: | 
 |       return update_delegatee_list(caveat_type, caveat, state->issued_time, | 
 |                                    result); | 
 |  | 
 |     // Time related caveats | 
 |     case kUwMacaroonCaveatTypeExpirationAbsolute: | 
 |       if (!uw_macaroon_caveat_get_value_uint_(caveat, &expiration_time)) { | 
 |         return false; | 
 |       } | 
 |       return update_and_check_expiration_time(context->current_time, | 
 |                                               expiration_time, result); | 
 |  | 
 |     // The caveats that update the values of the result object | 
 |     case kUwMacaroonCaveatTypeScope: | 
 |       if (!uw_macaroon_caveat_get_value_uint_(caveat, &scope) || | 
 |           // Larger value means less priviledge | 
 |           scope > UW_MACAROON_CAVEAT_SCOPE_LOWEST_POSSIBLE) { | 
 |         return false; | 
 |       } | 
 |       if (scope > (uint32_t)(result->granted_scope)) { | 
 |         result->granted_scope = (UwMacaroonCaveatScopeType)scope; | 
 |       } | 
 |       return true; | 
 |  | 
 |     case kUwMacaroonCaveatTypeAppCommandsOnly: | 
 |       result->app_commands_only = true; | 
 |       return true; | 
 |  | 
 |     case kUwMacaroonCaveatTypeLanSessionID: | 
 |       return uw_macaroon_caveat_get_value_bstr_( | 
 |           caveat, &(result->lan_session_id), &(result->lan_session_id_len)); | 
 |   } | 
 |  | 
 |   return false; | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_get_value_uint_(const UwMacaroonCaveat* caveat, | 
 |                                         uint32_t* unsigned_int) { | 
 |   if (caveat == NULL || unsigned_int == NULL) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   UwMacaroonCaveatType type; | 
 |   if (!uw_macaroon_caveat_get_type_(caveat, &type)) { | 
 |     return false; | 
 |   } | 
 |   if (type != kUwMacaroonCaveatTypeScope && | 
 |       type != kUwMacaroonCaveatTypeDelegateeService && | 
 |       type != kUwMacaroonCaveatTypeExpirationAbsolute && | 
 |       type != kUwMacaroonCaveatTypeDelegationTimestamp) { | 
 |     // Wrong type | 
 |     return false; | 
 |   } | 
 |  | 
 |   // Skip the portion for CBOR type | 
 |   size_t offset; | 
 |   if (!uw_macaroon_encoding_get_item_len_(caveat->bytes, caveat->num_bytes, | 
 |                                           &offset)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   return uw_macaroon_encoding_decode_uint_( | 
 |       caveat->bytes + offset, caveat->num_bytes - offset, unsigned_int); | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_get_value_bstr_(const UwMacaroonCaveat* caveat, | 
 |                                         const uint8_t** str, | 
 |                                         size_t* str_len) { | 
 |   if (caveat == NULL || str == NULL || str_len == NULL) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   UwMacaroonCaveatType type; | 
 |   if (!uw_macaroon_caveat_get_type_(caveat, &type)) { | 
 |     return false; | 
 |   } | 
 |   if (type != kUwMacaroonCaveatTypeNonce && | 
 |       type != kUwMacaroonCaveatTypeDelegateeUser && | 
 |       type != kUwMacaroonCaveatTypeDelegateeApp && | 
 |       type != kUwMacaroonCaveatTypeLanSessionID && | 
 |       type != kUwMacaroonCaveatTypeClientAuthorizationTokenV1 && | 
 |       type != kUwMacaroonCaveatTypeServerAuthenticationTokenV1) { | 
 |     // Wrong type | 
 |     return false; | 
 |   } | 
 |  | 
 |   size_t offset; | 
 |   if (!uw_macaroon_encoding_get_item_len_(caveat->bytes, caveat->num_bytes, | 
 |                                           &offset)) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   return uw_macaroon_encoding_decode_byte_str_( | 
 |       caveat->bytes + offset, caveat->num_bytes - offset, str, str_len); | 
 | } | 
 |  | 
 | bool uw_macaroon_caveat_init_validation_state_( | 
 |     UwMacaroonValidationState* state) { | 
 |   if (state == NULL) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   state->issued_time = 0; | 
 |   return true; | 
 | } |