Skip to content

Commit

Permalink
Binary format version 2 with tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
rescrv committed Jul 27, 2016
1 parent 2e511f1 commit be76d41
Show file tree
Hide file tree
Showing 49 changed files with 808 additions and 34 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
/libtool
/ltmain.sh
/m4
/macaroon-test-runner
/macaroon-test-verifier
/macaroon-test-serialization
/Makefile
/Makefile.in
/missing
Expand Down
33 changes: 26 additions & 7 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ TESTS_ENVIRONMENT = . $(abs_top_srcdir)/test/env.sh "${abs_top_srcdir}" "${abs_t

pyx_verbose = $(pyx_verbose_$(V))
pyx_verbose_ = $(pyx_verbose_$(AM_DEFAULT_VERBOSITY))
pyx_verbose_0 = @echo " PYX " $@;
pyx_verbose_0 = @echo " PYX " $@;

EXTRA_DIST =
EXTRA_DIST += README
Expand All @@ -53,6 +53,7 @@ libmacaroons_la_SOURCES += packet.c
libmacaroons_la_SOURCES += slice.c
libmacaroons_la_SOURCES += port.c
libmacaroons_la_SOURCES += v1.c
libmacaroons_la_SOURCES += v2.c
libmacaroons_la_SOURCES += varint.c
libmacaroons_la_SOURCES += explicit_bzero.c
libmacaroons_la_SOURCES += timingsafe_bcmp.c
Expand All @@ -71,10 +72,12 @@ EXTRA_DIST += test/python-hmac-sanity-check
EXTRA_DIST += test/python-hmac-sanity-check.sh
EXTRA_DIST += test/readme.sh

check_SCRIPTS =
check_LTLIBRARIES = libmacaroons-shim.la
check_PROGRAMS =
check_PROGRAMS += test/varint
check_PROGRAMS += macaroon-test-runner
check_PROGRAMS += macaroon-test-verifier
check_PROGRAMS += macaroon-test-serialization

libmacaroons_shim_la_SOURCES = shim.c explicit_bzero.c
libmacaroons_shim_la_LDFLAGS = -module -avoid-version -rpath /evil/libtool/hack/to/force/shared/lib/creation
Expand All @@ -89,22 +92,38 @@ TESTS += test/varint
test_varint_SOURCES = test/varint.c varint.c
test_varint_CFLAGS = $(AM_CFLAGS) $(CFLAGS)

macaroon_test_runner_SOURCES = test/macaroon-test-runner.c base64.c
macaroon_test_runner_LDADD = libmacaroons.la
macaroon_test_runner_CFLAGS = $(AM_CFLAGS) $(CFLAGS)
macaroon_test_verifier_SOURCES = macaroon-test-verifier.c base64.c
macaroon_test_verifier_LDADD = libmacaroons.la
macaroon_test_verifier_CFLAGS = $(AM_CFLAGS) $(CFLAGS)

macaroon_test_serialization_SOURCES = macaroon-test-serialization.c base64.c
macaroon_test_serialization_LDADD = libmacaroons.la
macaroon_test_serialization_CFLAGS = $(AM_CFLAGS) $(CFLAGS)

macaroon_unit_test_stubs =
macaroon_unit_test_stubs += test/unit/caveat_v1_1.vtest.sh
macaroon_unit_test_stubs += test/unit/caveat_v1_2.vtest.sh
macaroon_unit_test_stubs += test/unit/caveat_v1_3.vtest.sh
macaroon_unit_test_stubs += test/unit/caveat_v1_4.vtest.sh
macaroon_unit_test_stubs += test/unit/caveat_v1_5.vtest.sh
macaroon_unit_test_stubs += test/unit/caveat_v1_6.vtest.sh
macaroon_unit_test_stubs += test/unit/caveat_v2_1.vtest.sh
macaroon_unit_test_stubs += test/unit/caveat_v2_2.vtest.sh
macaroon_unit_test_stubs += test/unit/caveat_v2_3.vtest.sh
macaroon_unit_test_stubs += test/unit/caveat_v2_4.vtest.sh
macaroon_unit_test_stubs += test/unit/caveat_v2_5.vtest.sh
macaroon_unit_test_stubs += test/unit/caveat_v2_6.vtest.sh
macaroon_unit_test_stubs += test/unit/root_v1_1.vtest.sh
macaroon_unit_test_stubs += test/unit/root_v1_2.vtest.sh
macaroon_unit_test_stubs += test/unit/root_v2_1.vtest.sh
macaroon_unit_test_stubs += test/unit/root_v2_2.vtest.sh
macaroon_unit_test_stubs += test/unit/serialization_1.sh
macaroon_unit_test_stubs += test/unit/serialization_2.sh
macaroon_unit_test_stubs += test/unit/serialization_3.sh

TESTS += $(macaroon_unit_test_stubs)
check_SCRIPTS =
check_SCRIPTS += $(macaroon_unit_test_stubs)


#################################### Python ####################################

pyexec_LTLIBRARIES =
Expand Down
4 changes: 2 additions & 2 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ We can share this macaroon with others by serializing it. The serialized form
is pure-ASCII, and is safe for inclusion in secure email, a standard HTTPS
cookie, or a URL. We can get the serialized form with:

>>> M.serialize()
>>> M.serialize(format=1)
'MDAxY2xvY2F0aW9uIGh0dHA6Ly9teWJhbmsvCjAwMjZpZGVudGlmaWVyIHdlIHVzZWQgb3VyIHNlY3JldCBrZXkKMDAyZnNpZ25hdHVyZSDj2eApCFJsTAA5rhURQRXZf91ovyujebNCqvD2F9BVLwo'

Of course, this serialized form can be displayed in a more human-readable form
Expand Down Expand Up @@ -207,7 +207,7 @@ macaroon to the bank any time she wishes to prove to the bank that she is
authorized to access her account. Ideally, she'll transmit the serialized form
of the macaroon to the bank:

>>> msg = M.serialize()
>>> msg = M.serialize(format=1)
>>> # send msg to the bank

Verifying Macaroons
Expand Down
33 changes: 27 additions & 6 deletions bindings/python/macaroons.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,23 @@ cdef extern from "macaroons.h":
void macaroon_location(const macaroon* M, const unsigned char** location, size_t* location_sz)
void macaroon_identifier(const macaroon* M, const unsigned char** identifier, size_t* identifier_sz)
void macaroon_signature(const macaroon* M, const unsigned char** signature, size_t* signature_sz)
size_t macaroon_serialize_size_hint(macaroon* M)
int macaroon_serialize(macaroon* M, char* data, size_t data_sz, macaroon_returncode* err)
macaroon* macaroon_deserialize(unsigned char* data, size_t data_sz, macaroon_returncode* err)
size_t macaroon_inspect_size_hint(macaroon* M)
int macaroon_inspect(macaroon* M, char* data, size_t data_sz, macaroon_returncode* err)
macaroon* macaroon_copy(macaroon* M, macaroon_returncode* err)
int macaroon_cmp(macaroon* M, macaroon* N)

cdef enum macaroon_format:
MACAROON_V1
MACAROON_V2
MACAROON_V2J
cdef macaroon_format MACAROON_LATEST
cdef macaroon_format MACAROON_LATEST_JSON
size_t macaroon_serialize_size_hint(const macaroon* M, macaroon_format f);
size_t macaroon_serialize(const macaroon* M, macaroon_format f,
char* buf, size_t buf_sz,
macaroon_returncode* err);
macaroon* macaroon_deserialize(unsigned char* data, size_t data_sz, macaroon_returncode* err)


SUGGESTED_SECRET_LENGTH = 32

Expand Down Expand Up @@ -152,17 +161,18 @@ cdef class Macaroon:
raise_error(err)
return M

def serialize(self):
def serialize(self, format='latest'):
cdef macaroon_format f = self.version(format)
cdef char* data = NULL
cdef size_t data_sz = 0
cdef macaroon_returncode err
self.assert_not_null()
try:
data_sz = macaroon_serialize_size_hint(self._M)
data_sz = macaroon_serialize_size_hint(self._M, f)
data = <char*>malloc(sizeof(unsigned char) * data_sz)
if data == NULL:
raise MemoryError
if macaroon_serialize(self._M, data, data_sz, &err) < 0:
if macaroon_serialize(self._M, f, data, data_sz, &err) < 0:
raise_error(err)
return bytes(data)
finally:
Expand Down Expand Up @@ -251,6 +261,17 @@ cdef class Macaroon:
if self._M == NULL:
raise ValueError("macaroon not initialized")

cdef macaroon_format version(self, v):
cdef macaroon_format f = {'latest': MACAROON_LATEST,
'binary': MACAROON_LATEST,
'json': MACAROON_LATEST_JSON,
'2j': MACAROON_V2J,
2: MACAROON_V2,
'2': MACAROON_V2,
1: MACAROON_V1,
'1': MACAROON_V1}[v]
return f


cdef int general_cb(void* f, const unsigned char* pred, size_t pred_sz):
try:
Expand Down
23 changes: 22 additions & 1 deletion doc/tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,25 @@ their equivalents in other implementations):
Serialization Tests
-------------------

XXX write these and some tests
Serialization tests provide serialized macaroons for reference. Each file
contains a set of macaroons that are serialized differently, but represent the
same logical macaroon. It is up to the implementation to decide how to use
these reference macaroons. It is suggested that an implementation:

- Deserialize each supported macaroon format and compare them the in-memory
structures to each other to confirm that they are equivalent
- Serialize each macaroon into each format and confirm that the output
matches the reference form. Failing that, the implementation should
serialize and deserialize the macaroon repeatedly, verifying that each
serialized form deserializes to something equivalent to the macaroon. The
implementation need only repeat until a previously serialized form is
encountered.

The serialization format contains one macaroon per line, where each macaroon
is labelled with the version of the macaroon. Each macaroon is base-64
encoded to avoid having to parse binary data from the test file. The decoded
form of the line corresponds to a macaroon in one of the serialized formats
specified in "version". A sample file looks like this:

v1 TURBeU1XeHZZMkYwYVc5dUlHaDBkSEE2THk5bGVHRnRjR3hsTG05eVp5OEtNREF4Tldsa1pXNTBhV1pwWlhJZ2EyVjVhV1FLTURBeVpuTnBaMjVoZEhWeVpTQjgzdWVTVVJ4Ynh2VW9TRmdGMy1teVRuaGVLT0twa3dINTF4SEdDZU9POXdv
v2 AgETaHR0cDovL2V4YW1wbGUub3JnLwIFa2V5aWQAAAAGIHze55JRHFvG9ShIWAXf6bJOeF4o4qmTAfnXEcYJ4473
161 changes: 161 additions & 0 deletions macaroon-test-serialization.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/* Copyright (c) 2016, Robert Escriva
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of this project nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

#define _WITH_GETLINE

/* C */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* macaroons */
#include "macaroons.h"
#include "base64.h"

struct parsed_macaroon
{
enum macaroon_format F;
struct macaroon* M;
};

int
main(int argc, const char* argv[])
{
char* line = NULL;
size_t line_sz = 0;
struct parsed_macaroon* macaroons = NULL;
size_t macaroons_sz = 0;

while (1)
{
ssize_t amt = getline(&line, &line_sz, stdin);

if (amt < 0)
{
if (feof(stdin) != 0)
{
break;
}

fprintf(stderr, "could not read from stdin: %s\n", strerror(ferror(stdin)));
return EXIT_FAILURE;
}

if (!line || amt == 0 || *line == '\n' || *line == '#')
{
continue;
}

line[amt - 1] = '\0';
char* space = strchr(line, ' ');
char* const end = line + amt - 1;

if (!space)
{
fprintf(stderr, "space missing on line %lu\n", macaroons_sz + 1);
return EXIT_FAILURE;
}

assert(space < end);
*space = '\0';
enum macaroon_format format;

if (strcmp(line, "v1") == 0)
{
format = MACAROON_V1;
}
else if (strcmp(line, "v2") == 0)
{
format = MACAROON_V2;
}
else if (strcmp(line, "v2j") == 0)
{
format = MACAROON_V2J;
}
else
{
fprintf(stderr, "version %s not supported\n", line);
return EXIT_FAILURE;
}

size_t buf_sz = strlen(space + 1);
unsigned char* buf = malloc(buf_sz);

if (!buf)
{
return EXIT_FAILURE;
}

int rc = b64_pton(space + 1, buf, buf_sz);

if (rc < 0)
{
fprintf(stderr, "could not unwrap serialized macaroon\n");
return EXIT_FAILURE;
}

enum macaroon_returncode err;
struct macaroon* M = macaroon_deserialize(buf, rc, &err);

if (!M)
{
fprintf(stderr, "could not deserialize macaroon: %s\n", macaroon_error(err));
return EXIT_FAILURE;
}

++macaroons_sz;
macaroons = realloc(macaroons, macaroons_sz * sizeof(struct parsed_macaroon));

if (!macaroons)
{
return EXIT_FAILURE;
}

macaroons[macaroons_sz - 1].F = format;
macaroons[macaroons_sz - 1].M = M;
}

int ret = EXIT_SUCCESS;

for (size_t i = 0; i < macaroons_sz; ++i)
{
for (size_t j = i + 1; j < macaroons_sz; ++j)
{
if (macaroon_cmp(macaroons[i].M, macaroons[j].M) != 0)
{
printf("macaroons %lu and %lu do not match\n", i, j);
ret = EXIT_FAILURE;
}
}
}

(void) argc;
(void) argv;
return ret;
}
File renamed without changes.
Loading

0 comments on commit be76d41

Please sign in to comment.