blob: 007aa155c46f723716bddd30355c73de43a5250f [file] [log] [blame]
Vitaly Buka45dc9df2015-12-07 21:30:19 -08001// Copyright 2015 The Weave Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/macaroon.h"
6
7#include <string.h>
8
9#include "src/crypto_utils.h"
Vitaly Buka6a8bd5d2015-12-08 21:06:59 -080010#include "src/macaroon_caveat.h"
Vitaly Buka7d29a5a2016-01-27 14:21:37 -080011#include "src/macaroon_caveat_internal.h"
Vitaly Buka6a8bd5d2015-12-08 21:06:59 -080012#include "src/macaroon_encoding.h"
Vitaly Buka45dc9df2015-12-07 21:30:19 -080013
Vitaly Buka7d29a5a2016-01-27 14:21:37 -080014static bool create_mac_tag_(const uint8_t* key,
15 size_t key_len,
16 const UwMacaroonContext* context,
17 const UwMacaroonCaveat* const caveats[],
18 size_t num_caveats,
Vitaly Buka45dc9df2015-12-07 21:30:19 -080019 uint8_t mac_tag[UW_MACAROON_MAC_LEN]) {
Vitaly Buka7d29a5a2016-01-27 14:21:37 -080020 if (key == NULL || key_len == 0 || context == NULL || caveats == NULL ||
21 num_caveats == 0 || mac_tag == NULL) {
Vitaly Buka45dc9df2015-12-07 21:30:19 -080022 return false;
23 }
24
25 // Store the intermediate MAC tags in an internal buffer before we finish the
26 // whole computation.
27 // If we use the output buffer mac_tag directly and certain errors happen in
28 // the middle of this computation, mac_tag will probably contain a valid
29 // macaroon tag with large scope than expected.
30 uint8_t mac_tag_buff[UW_MACAROON_MAC_LEN];
31
32 // Compute the first tag by using the key
Vitaly Buka7d29a5a2016-01-27 14:21:37 -080033 if (!uw_macaroon_caveat_sign_(key, key_len, context, caveats[0], mac_tag_buff,
Vitaly Buka98af48b2016-03-02 11:10:39 -080034 sizeof(mac_tag_buff))) {
Vitaly Buka45dc9df2015-12-07 21:30:19 -080035 return false;
36 }
37
38 // Compute the rest of the tags by using the tag as the key
39 for (size_t i = 1; i < num_caveats; i++) {
Vitaly Buka98af48b2016-03-02 11:10:39 -080040 if (!uw_macaroon_caveat_sign_(mac_tag_buff, sizeof(mac_tag_buff), context,
Vitaly Buka7d29a5a2016-01-27 14:21:37 -080041 caveats[i], mac_tag_buff,
Vitaly Buka98af48b2016-03-02 11:10:39 -080042 sizeof(mac_tag_buff))) {
Vitaly Buka45dc9df2015-12-07 21:30:19 -080043 return false;
44 }
45 }
46
47 memcpy(mac_tag, mac_tag_buff, UW_MACAROON_MAC_LEN);
48 return true;
49}
50
Vitaly Buka7d29a5a2016-01-27 14:21:37 -080051static bool verify_mac_tag_(const uint8_t* root_key,
52 size_t root_key_len,
53 const UwMacaroonContext* context,
54 const UwMacaroonCaveat* const caveats[],
55 size_t num_caveats,
56 const uint8_t mac_tag[UW_MACAROON_MAC_LEN]) {
57 if (root_key == NULL || root_key_len == 0 || context == NULL ||
58 caveats == NULL || num_caveats == 0 || mac_tag == 0) {
Vitaly Buka45dc9df2015-12-07 21:30:19 -080059 return false;
60 }
61
Vitaly Buka7d29a5a2016-01-27 14:21:37 -080062 uint8_t computed_mac_tag[UW_MACAROON_MAC_LEN] = {0};
63 if (!create_mac_tag_(root_key, root_key_len, context, caveats, num_caveats,
64 computed_mac_tag)) {
65 return false;
66 }
Vitaly Buka45dc9df2015-12-07 21:30:19 -080067
Vitaly Buka7d29a5a2016-01-27 14:21:37 -080068 return uw_crypto_utils_equal_(mac_tag, computed_mac_tag, UW_MACAROON_MAC_LEN);
Vitaly Buka45dc9df2015-12-07 21:30:19 -080069}
70
Vitaly Buka7d29a5a2016-01-27 14:21:37 -080071bool uw_macaroon_create_from_root_key_(UwMacaroon* new_macaroon,
72 const uint8_t* root_key,
73 size_t root_key_len,
74 const UwMacaroonContext* context,
75 const UwMacaroonCaveat* const caveats[],
76 size_t num_caveats) {
77 if (new_macaroon == NULL || root_key == NULL || context == NULL ||
78 root_key_len == 0 || caveats == NULL || num_caveats == 0) {
Vitaly Buka45dc9df2015-12-07 21:30:19 -080079 return false;
80 }
81
Vitaly Buka7d29a5a2016-01-27 14:21:37 -080082 if (!create_mac_tag_(root_key, root_key_len, context, caveats, num_caveats,
Vitaly Buka45dc9df2015-12-07 21:30:19 -080083 new_macaroon->mac_tag)) {
84 return false;
85 }
86
87 new_macaroon->num_caveats = num_caveats;
88 new_macaroon->caveats = caveats;
89
90 return true;
91}
92
Vitaly Buka45dc9df2015-12-07 21:30:19 -080093bool uw_macaroon_extend_(const UwMacaroon* old_macaroon,
94 UwMacaroon* new_macaroon,
Vitaly Buka7d29a5a2016-01-27 14:21:37 -080095 const UwMacaroonContext* context,
Vitaly Buka45dc9df2015-12-07 21:30:19 -080096 const UwMacaroonCaveat* additional_caveat,
Vitaly Buka7d29a5a2016-01-27 14:21:37 -080097 uint8_t* buffer,
98 size_t buffer_size) {
99 if (old_macaroon == NULL || new_macaroon == NULL || context == NULL ||
Vitaly Buka45dc9df2015-12-07 21:30:19 -0800100 additional_caveat == NULL || buffer == NULL || buffer_size == 0) {
101 return false;
102 }
Vitaly Buka98af48b2016-03-02 11:10:39 -0800103 // If we update the same macaroon in-place, do not zero it.
104 if (new_macaroon != old_macaroon) {
105 *new_macaroon = (UwMacaroon){};
106 }
Vitaly Buka45dc9df2015-12-07 21:30:19 -0800107
Vitaly Buka98af48b2016-03-02 11:10:39 -0800108 const size_t old_count = old_macaroon->num_caveats;
109 const size_t new_count = old_count + 1;
110
111 new_macaroon->num_caveats = new_count;
Vitaly Buka45dc9df2015-12-07 21:30:19 -0800112
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800113 // Extend the caveat pointer list
Vitaly Buka98af48b2016-03-02 11:10:39 -0800114 if (new_count * sizeof(new_macaroon->caveats[0]) > buffer_size) {
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800115 // Not enough memory to store the extended caveat pointer list
Vitaly Buka45dc9df2015-12-07 21:30:19 -0800116 return false;
117 }
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800118 const UwMacaroonCaveat** extended_list = (const UwMacaroonCaveat**)buffer;
Vitaly Buka98af48b2016-03-02 11:10:39 -0800119 if (extended_list != old_macaroon->caveats) {
Vitaly Buka45dc9df2015-12-07 21:30:19 -0800120 memcpy(extended_list, old_macaroon->caveats,
Vitaly Buka98af48b2016-03-02 11:10:39 -0800121 old_count * sizeof(old_macaroon->caveats[0]));
Vitaly Buka45dc9df2015-12-07 21:30:19 -0800122 }
Vitaly Buka98af48b2016-03-02 11:10:39 -0800123 extended_list[old_count] = additional_caveat;
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800124 new_macaroon->caveats = (const UwMacaroonCaveat* const*)extended_list;
Vitaly Buka45dc9df2015-12-07 21:30:19 -0800125
126 // Compute the new MAC tag
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800127 return create_mac_tag_(old_macaroon->mac_tag, UW_MACAROON_MAC_LEN, context,
Vitaly Buka98af48b2016-03-02 11:10:39 -0800128 new_macaroon->caveats + old_count, 1,
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800129 new_macaroon->mac_tag);
130}
131
132static void init_validation_result(UwMacaroonValidationResult* result) {
133 // Start from the largest scope
Vitaly Buka08be74d2016-02-02 15:25:09 -0800134 *result = (UwMacaroonValidationResult){
135 .granted_scope = kUwMacaroonCaveatScopeTypeOwner,
136 .expiration_time = UINT32_MAX,
137 };
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800138}
139
140/** Reset the result object to the lowest scope when encountering errors */
141static void reset_validation_result(UwMacaroonValidationResult* result) {
Vitaly Buka08be74d2016-02-02 15:25:09 -0800142 *result = (UwMacaroonValidationResult){
Vitaly Buka98af48b2016-03-02 11:10:39 -0800143 .app_commands_only = true,
Vitaly Buka08be74d2016-02-02 15:25:09 -0800144 .granted_scope = UW_MACAROON_CAVEAT_SCOPE_LOWEST_POSSIBLE};
145}
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800146
Vitaly Buka08be74d2016-02-02 15:25:09 -0800147/** Get the next closest scope (to the narrower side). */
148static UwMacaroonCaveatScopeType get_closest_scope(
149 UwMacaroonCaveatScopeType scope) {
150 if (scope <= kUwMacaroonCaveatScopeTypeOwner) {
151 return kUwMacaroonCaveatScopeTypeOwner;
152 } else if (scope <= kUwMacaroonCaveatScopeTypeManager) {
153 return kUwMacaroonCaveatScopeTypeManager;
154 } else if (scope <= kUwMacaroonCaveatScopeTypeUser) {
155 return kUwMacaroonCaveatScopeTypeUser;
156 } else if (scope <= kUwMacaroonCaveatScopeTypeViewer) {
157 return kUwMacaroonCaveatScopeTypeViewer;
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800158 }
Vitaly Buka08be74d2016-02-02 15:25:09 -0800159 return scope;
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800160}
161
162bool uw_macaroon_validate_(const UwMacaroon* macaroon,
163 const uint8_t* root_key,
164 size_t root_key_len,
165 const UwMacaroonContext* context,
166 UwMacaroonValidationResult* result) {
167 if (result == NULL) {
168 return false;
169 }
170 init_validation_result(result);
171
172 if (root_key == NULL || root_key_len == 0 || macaroon == NULL ||
173 context == NULL || result == NULL ||
174 !verify_mac_tag_(root_key, root_key_len, context, macaroon->caveats,
175 macaroon->num_caveats, macaroon->mac_tag)) {
176 return false;
177 }
178
179 UwMacaroonValidationState state;
180 if (!uw_macaroon_caveat_init_validation_state_(&state)) {
181 return false;
182 }
183 for (size_t i = 0; i < macaroon->num_caveats; i++) {
184 if (!uw_macaroon_caveat_validate_(macaroon->caveats[i], context, &state,
185 result)) {
186 reset_validation_result(result); // Reset the result object
187 return false;
188 }
189 }
190
Vitaly Buka08be74d2016-02-02 15:25:09 -0800191 result->granted_scope = get_closest_scope(result->granted_scope);
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800192 return true;
Vitaly Buka45dc9df2015-12-07 21:30:19 -0800193}
Vitaly Buka6a8bd5d2015-12-08 21:06:59 -0800194
195// Encode a Macaroon to a byte string
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800196bool uw_macaroon_serialize_(const UwMacaroon* macaroon,
197 uint8_t* out,
198 size_t out_len,
199 size_t* resulting_str_len) {
200 if (macaroon == NULL || out == NULL ||
201 out_len < UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN ||
Vitaly Buka6a8bd5d2015-12-08 21:06:59 -0800202 resulting_str_len == NULL) {
203 return false;
204 }
205
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800206 // Need to encode the whole Macaroon again into a byte string.
Vitaly Buka6a8bd5d2015-12-08 21:06:59 -0800207
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800208 // First encode the part without the overall byte string header to the buffer
209 // to get the total length.
210 size_t item_len = 0;
211 // Start with an offset
212 size_t offset = UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN;
213 if (!uw_macaroon_encoding_encode_array_len_((uint32_t)(macaroon->num_caveats),
214 out + offset, out_len - offset,
215 &item_len)) {
Vitaly Buka6a8bd5d2015-12-08 21:06:59 -0800216 return false;
217 }
218 offset += item_len;
219
220 for (size_t i = 0; i < macaroon->num_caveats; i++) {
221 if (!uw_macaroon_encoding_encode_byte_str_(
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800222 macaroon->caveats[i]->bytes, macaroon->caveats[i]->num_bytes,
Vitaly Buka6a8bd5d2015-12-08 21:06:59 -0800223 out + offset, out_len - offset, &item_len)) {
224 return false;
225 }
226 offset += item_len;
227 }
228
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800229 if (!uw_macaroon_encoding_encode_byte_str_(macaroon->mac_tag,
230 UW_MACAROON_MAC_LEN, out + offset,
231 out_len - offset, &item_len)) {
232 return false;
233 }
234 offset += item_len;
235
236 // Encode the length of the body at the beginning of the buffer
237 size_t bstr_len = offset - UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN;
238 if (!uw_macaroon_encoding_encode_byte_str_len_(
239 bstr_len, out, UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN, &item_len)) {
240 return false;
241 }
242
243 // Move the body part to be adjacent to the byte string header part
244 memmove(out + item_len, out + UW_MACAROON_ENCODING_MAX_UINT_CBOR_LEN,
245 bstr_len);
246
247 *resulting_str_len = item_len + bstr_len;
Vitaly Buka6a8bd5d2015-12-08 21:06:59 -0800248 return true;
249}
250
251// Decode a byte string to a Macaroon
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800252bool uw_macaroon_deserialize_(const uint8_t* in,
253 size_t in_len,
254 uint8_t* buffer,
255 size_t buffer_size,
256 UwMacaroon* macaroon) {
257 if (in == NULL || in_len == 0 || buffer == NULL || buffer_size == 0 ||
258 macaroon == NULL) {
Vitaly Buka6a8bd5d2015-12-08 21:06:59 -0800259 return false;
260 }
261
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800262 size_t offset = 0;
263 size_t item_len = 0;
264
265 const uint8_t* bstr = NULL;
266 size_t bstr_len = 0;
267 if (!uw_macaroon_encoding_decode_byte_str_(in + offset, in_len - offset,
268 &bstr, &bstr_len)) {
Vitaly Buka6a8bd5d2015-12-08 21:06:59 -0800269 return false;
270 }
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800271 item_len = bstr - in; // The length of the first byte string header
272 offset += item_len;
Vitaly Buka6a8bd5d2015-12-08 21:06:59 -0800273
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800274 if (item_len + bstr_len != in_len) {
275 // The string length doesn't match
Vitaly Buka6a8bd5d2015-12-08 21:06:59 -0800276 return false;
277 }
Vitaly Buka6a8bd5d2015-12-08 21:06:59 -0800278
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800279 uint32_t array_len = 0;
Vitaly Buka6a8bd5d2015-12-08 21:06:59 -0800280 if (!uw_macaroon_encoding_decode_array_len_(in + offset, in_len - offset,
281 &array_len)) {
282 return false;
283 }
284 macaroon->num_caveats = (size_t)array_len;
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800285 if (buffer_size <
286 (array_len * (sizeof(UwMacaroonCaveat) + sizeof(UwMacaroonCaveat*)))) {
287 // Need two levels of abstraction, one for structs and one for pointers
Vitaly Buka6a8bd5d2015-12-08 21:06:59 -0800288 return false;
289 }
290
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800291 if (!uw_macaroon_encoding_get_item_len_(in + offset, in_len - offset,
292 &item_len)) {
293 return false;
Vitaly Buka6a8bd5d2015-12-08 21:06:59 -0800294 }
Vitaly Buka7d29a5a2016-01-27 14:21:37 -0800295 offset += item_len;
296
297 const UwMacaroonCaveat** caveat_pointers = (const UwMacaroonCaveat**)buffer;
298 buffer += array_len * sizeof(UwMacaroonCaveat*);
299 UwMacaroonCaveat* caveat_structs = (UwMacaroonCaveat*)buffer;
300 for (size_t i = 0; i < array_len; i++) {
301 caveat_pointers[i] = &(caveat_structs[i]);
302
303 if (!uw_macaroon_encoding_decode_byte_str_(
304 in + offset, in_len - offset, &(caveat_structs[i].bytes),
305 &(caveat_structs[i].num_bytes))) {
306 return false;
307 }
308
309 if (!uw_macaroon_encoding_get_item_len_(in + offset, in_len - offset,
310 &item_len)) {
311 return false;
312 }
313 offset += item_len;
314 }
315 macaroon->caveats = caveat_pointers;
316
317 const uint8_t* tag;
318 size_t tag_len;
319 if (!uw_macaroon_encoding_decode_byte_str_(in + offset, in_len - offset, &tag,
320 &tag_len) ||
321 tag_len != UW_MACAROON_MAC_LEN) {
322 return false;
323 }
324 memcpy(macaroon->mac_tag, tag, UW_MACAROON_MAC_LEN);
Vitaly Buka6a8bd5d2015-12-08 21:06:59 -0800325
326 return true;
327}
Vitaly Buka98af48b2016-03-02 11:10:39 -0800328
329time_t uw_macaroon_get_expiration_unix_epoch_time_(
330 UwMacaroonValidationResult* result) {
331 // Expiration times of 0 and UINT32_MAX both mean no expiration.
332 if (result->expiration_time == 0 || result->expiration_time == UINT32_MAX) {
333 return 0;
334 }
335 return uw_macaroon_j2000_to_unix_epoch(result->expiration_time);
336}