blob: bd1011b85dad2e23dafab444ef1ed9b809db70b2 [file] [log] [blame]
// 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;
}