pax_global_header00006660000000000000000000000064125311116660014514gustar00rootroot0000000000000052 comment=768be91aa77b2627291e110dc5a9dc5c244ffb6e libmacaroons-releases-0.3.0/000077500000000000000000000000001253111166600157665ustar00rootroot00000000000000libmacaroons-releases-0.3.0/.gitignore000066400000000000000000000005201253111166600177530ustar00rootroot00000000000000# wildcards .deps .dirstamp *.la .libs *.lo *.o # specific files /aclocal.m4 /autom4te.cache /bindings/python/macaroons.c /compile /config.guess /config.h /config.h.in /config.h.in~ /config.log /config.status /config.sub /configure /depcomp /install-sh /libmacaroons.pc /libtool /ltmain.sh /m4 /Makefile /Makefile.in /missing /stamp-h1 libmacaroons-releases-0.3.0/.tarballignore000066400000000000000000000000321253111166600206070ustar00rootroot00000000000000.gitignore .tarballignore libmacaroons-releases-0.3.0/LICENSE000066400000000000000000000027511253111166600170000ustar00rootroot00000000000000Copyright (c) 2014, 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. libmacaroons-releases-0.3.0/Makefile.am000066400000000000000000000077071253111166600200350ustar00rootroot00000000000000# Copyright (c) 2014, 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. ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} AM_MAKEFLAGS = --no-print-directory AM_CPPFLAGS = $(SODIUM_CFLAGS) AM_CFLAGS = -fvisibility=hidden $(SODIUM_CFLAGS) $(WANAL_CFLAGS) AM_CXXFLAGS = -fvisibility=hidden -fvisibility-inlines-hidden $(SODIUM_CFLAGS) $(WANAL_CXXFLAGS) AM_DISTCHECK_CONFIGURE_FLAGS = --enable-python-bindings TESTS_ENVIRONMENT = . $(abs_top_srcdir)/test/env.sh "${abs_top_srcdir}" "${abs_top_builddir}" "${VERSION}"; pyx_verbose = $(pyx_verbose_$(V)) pyx_verbose_ = $(pyx_verbose_$(AM_DEFAULT_VERBOSITY)) pyx_verbose_0 = @echo " PYX " $@; EXTRA_DIST = EXTRA_DIST += README EXTRA_DIST += LICENSE include_HEADERS = macaroons.h noinst_HEADERS = base64.h constants.h custom-config.h packet.h port.h lib_LTLIBRARIES = libmacaroons.la libmacaroons_la_SOURCES = base64.c macaroons.c packet.c port.c libmacaroons_la_LIBADD = libmacaroons_la_LIBADD += $(SODIUM_LIBS) if ENABLE_JSON_SUPPORT libmacaroons_la_LIBADD += $(JSON_LIBS) endif libmacaroons_la_LDFLAGS = -version-info 0:1:0 pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libmacaroons.pc ##################################### Tests #################################### EXTRA_DIST += test/env.sh EXTRA_DIST += test/python-hmac-sanity-check EXTRA_DIST += test/python-hmac-sanity-check.sh EXTRA_DIST += test/readme.sh TESTS = TESTS += test/python-hmac-sanity-check.sh #TESTS += test/readme.sh #################################### Python #################################### pyexec_LTLIBRARIES = if ENABLE_PYTHON_BINDINGS pyexec_LTLIBRARIES += bindings/python/macaroons.la endif EXTRA_DIST += bindings/python/macaroons.pyx bindings_python_macaroons_la_SOURCES = bindings/python/macaroons.c bindings_python_macaroons_la_CPPFLAGS = bindings_python_macaroons_la_CPPFLAGS += $(PYTHON_CPPFLAGS) bindings_python_macaroons_la_CPPFLAGS += $(AM_CPPFLAGS) bindings_python_macaroons_la_CPPFLAGS += $(CPPFLAGS) bindings_python_macaroons_la_CFLAGS = bindings_python_macaroons_la_CFLAGS += -fvisibility=default bindings_python_macaroons_la_CFLAGS += -fno-strict-aliasing bindings_python_macaroons_la_CFLAGS += $(CFLAGS) bindings_python_macaroons_la_LIBADD = bindings_python_macaroons_la_LIBADD += libmacaroons.la bindings_python_macaroons_la_LIBADD += $(PYTHON_LDFLAGS) bindings_python_macaroons_la_LDFLAGS = -module -avoid-version -export-symbols-regex initmacaroons $(AM_LDFLAGS) $(LDFLAGS) bindings/python/macaroons.c: bindings/python/macaroons.pyx $(pyx_verbose)cython bindings/python/macaroons.pyx libmacaroons-releases-0.3.0/README000066400000000000000000000717721253111166600166640ustar00rootroot00000000000000Macaroons are Better Than Cookies! ================================== This library provides an implementation of macaroons[1], which are flexible authorization tokens that work great in distributed systems. Like cookies, macaroons are bearer tokens that enable applications to ascertain whether their holders' actions are authorized. But macaroons are better than cookies! Why Macaroons? -------------- Macaroons are great for authorization because they're similar enough to cookies to be immediately usable by developers, but they include several features not present in cookies or other token-based authorization schemes. In particular: - Delegation with Contextual Caveats (i.e., confinement of the usage context): Macaroons support delegation. Give your macaroon to another user, and they can act on your behalf, with the same authority. Cookies permit delegation as well, but the remaining features of macaroons make it much more safe and practical to pass around macaroons than cookies. In particular, macaroons can limit when, where, and by whom the delegated authority can be exercised (e.g., within one minute, from a machine that holds a certain key, or by a certain logged-in user), by using attenuation and third-party caveats. - Attenuation: Macaroons enable users to add caveats to the macaroon that attenuate how, when, and where it may be used. Unlike cookies, macaroons permit their holder to attenuate them before delegating. Whereas cookies and authorization tokens enable an application to get access to all of your data and to perform actions on your behalf with your full privileges, macaroons enable you to restrict what they can do. Those questionable startups that "just want the address book, we swear it," become a whole lot more secure when the target application supports macaroons, because macaroons enable you to add caveats that restrict what the application can do. - Proof-Carrying: Macaroons are efficient, because they carry their own proof of authorization---cryptographically secured, of course. A macaroon's caveats are constructed using chained HMAC functions, which makes it really easy to add a caveat, but impossible to remove a caveat. When you attenuate a macaroon and give it to another application, there is no way to strip the caveats from the macaroon. It's easy for the entity that created a macaroon to verify the embedded proof, but others cannot. - Third-Party Caveats: Macaroons allow caveats to specify predicates that are enforced by third parties. A macaroon with a third-party caveat will only be authorized when the third party certifies that the caveat is satisfied. This enables loosely coupled distributed systems to work together to authorize requests. For example, a data store can provide macaroons that are authorized if and only if the application's authentication service says that the user is authenticated. The user obtains a proof that it is authenticated from the authentication service, and presents this proof alongside the original macaroon to the storage service. The storage service can verify that the user is indeed authenticated, without knowing anything about the authentication service's implementation---in a standard implementation, the storage service can authorize the request without even communicating with the authentication service. - Simple Verification: Macaroons eliminate complexity in the authorization code of your application. Instead of hand-coding complex conditionals in each routine that deals with authorization, and hoping that this logic is globally consistent, you construct a general verifier for macaroons. This verifier knows how to soundly check the proofs embedded within macaroons to see if they do indeed authorize access. - Decoupled Authorization Logic: Macaroons separate the policy of your application (who can access what, when), from the mechanism (the code that actually upholds this policy). Because of the way the verifier is constructed, it is agnostic to the actual underlying policies it is enforcing. It simply observes the policy (in the form of an embedded proof) and certifies that the proof is correct. The policy itself is specified when macaroons are created, attenuated, and shared. You can easily audit this code within your application, and ensure that it is upheld everywhere. The rest of this document walks through the specifics of macaroons and see just how easy authorization can be. So pour a fresh cup of espresso to enjoy alongside your macaroons and read on! Installing Macaroons -------------------- This library makes it easy to get started with using macaroons in your service. To use the library you must first install it. You'll need to somehow install libsodium[2]. It's packaged in some Linux distributions, and can be installed from source on most *NIX platforms. Once you have libsodium installed, installing macaroons is straight forward: $ autoreconf -i # only when installing from Git $ ./configure --enable-python-bindings $ make # make install This will install macaroons onto your system and give you both the C and Python interfaces to libmacaroons. In the rest of this document, we'll show examples using the Python interface, but the code could easily be translated into C. Creating Your First Macaroon ---------------------------- Imagine that you ran a bank, and were looking to use macaroons as the basis of your authorization logic. Assuming you already installed libmacaroons, you can create a macaroon like this: >>> import macaroons >>> secret = 'this is our super secret key; only we should know it' >>> public = 'we used our secret key' >>> location = 'http://mybank/' >>> M = macaroons.create(location, secret, public) We've created our first macaroon! You can see here that it took three pieces of information to create a macaroon. We start with a secret. Here, we just have English text, but in reality we would want to use something more random and less predictable (see below for a suggestion on how to generate one). The public portion tells us which secret we used to create the macaroon, but doesn't give anyone else a clue as to the contents of the secret. Anyone in possession of the macaroon can see the public portion: >>> M.identifier 'we used our secret key' This public portion, known as the macaroon's identifier, can be anything that enables us to remember our secret. In this example, we know that the string 'we used our secret key' always refers to this secret. We could just as easily keep a database mapping public macaroon identities to private secrets, or encrypt the public portion using a key known only to us. The only requirement here is that our application be able to remember the secret given the public portion, and that no one else is able to guess the secret. Our macaroon includes some additional information that enables us to add caveats and figure out where the macaroon should be used. The macaroon's location gives a hint to the user about where the macaroon is accepted for authorization. This hint is a free-form string maintained to help applications figure out where to use macaroons; the libmacaroons library (and by extension, the Python bindings) do not ascribe any meaning to this location. Each macaroon also has a signature that is the key used to add caveats and verify the macaroon. The signature is computed by the macaroons library, and is unique to each macaroon. Applications should never need to directly work with the signature of the macaroon. Any entity in possession of the macaroon's signature should be assumed to possess the macaroon itself. Both of these pieces of information are publicly accessible: >>> M.location 'http://mybank/' >>> M.signature 'e3d9e02908526c4c0039ae15114115d97fdd68bf2ba379b342aaf0f617d0552f' 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() 'MDAxY2xvY2F0aW9uIGh0dHA6Ly9teWJhbmsvCjAwMjZpZGVudGlmaWVyIHdlIHVzZWQgb3VyIHNlY3JldCBrZXkKMDAyZnNpZ25hdHVyZSDj2eApCFJsTAA5rhURQRXZf91ovyujebNCqvD2F9BVLwo' Of course, this serialized form can be displayed in a more human-readable form for easy debugging: >>> print M.inspect() location http://mybank/ identifier we used our secret key signature e3d9e02908526c4c0039ae15114115d97fdd68bf2ba379b342aaf0f617d0552f Adding Caveats -------------- At this point, we have an unconstrained macaroon that authorizes everything within our bank. In practice, such a macaroon is dangerous, because very few people should hold such power. Let's add a caveat to our macaroon that restricts it to just the account number 3735928559. >>> M = M.add_first_party_caveat('account = 3735928559') This new macaroon includes the same identifier and location that our old macaroon from our initial macaroon, but includes the additional caveat that restricts the bank account. The signature of this new macaroon is different, and incorporates the new caveat we've just added. An entity in possession of this new macaroon cannot simply remove our new caveat to construct the old macaroon: >>> print M.inspect() location http://mybank/ identifier we used our secret key cid account = 3735928559 signature 1efe4763f290dbce0c1d08477367e11f4eee456a64933cf662d79772dbb82128 Of course, we can add a few more caveats, and the macaroon's signature will change with each of them. >>> M = M.add_first_party_caveat('time < 2020-01-01T00:00') >>> M.signature 'b5f06c8c8ef92f6c82c6ff282cd1f8bd1849301d09a2db634ba182536a611c49' >>> M = M.add_first_party_caveat('email = alice@example.org') >>> M.signature 'ddf553e46083e55b8d71ab822be3d8fcf21d6bf19c40d617bb9fb438934474b6' >>> print M.inspect() location http://mybank/ identifier we used our secret key cid account = 3735928559 cid time < 2020-01-01T00:00 cid email = alice@example.org signature ddf553e46083e55b8d71ab822be3d8fcf21d6bf19c40d617bb9fb438934474b6 The combination of all caveats in this macaroon authorize alice@example.org to access account 3735928559 until the end of 2014. Alice may present this 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() >>> # send msg to the bank Verifying Macaroons ------------------- Our bank application's purpose is to protect users accounts from unauthorized access. For that reason, it cannot just accept anything that looks like a macaroon---that would defeat the point of using macaroons in the first place. So how can we ensure that only authorized users access the bank? We can determine whether a request is authorized through a process called verification. First, we construct a verifier that can determine whether the caveats on macaroons are satisfied. We can then use our verifier to determine whether a given macaroon is authorized in the context of the request. For example, our bank account application knows the account number specified in the request, and can specify ``account = #'' when building the verifier. The verifier can then check that this matches the information within the macaroon, and authorize the macaroon if it does indeed match. Let's walk through the verification process for Alice's macaroon that we constructed in the previous section. The first step, of course, is for the bank to deserialize the macaroon from the message. This converts the macaroon into a form we can work with. >>> M = macaroons.deserialize(msg) >>> print M.inspect() location http://mybank/ identifier we used our secret key cid account = 3735928559 cid time < 2020-01-01T00:00 cid email = alice@example.org signature ddf553e46083e55b8d71ab822be3d8fcf21d6bf19c40d617bb9fb438934474b6 We have the same macaroon that Alice believes authorizes her to access her own account, but we must verify this for ourselves. One (very flawed) way we could try to verify this macaroon would be to manually parse it and authorize the request if its caveats are true. But handling things this way completely sidesteps all the crypto-goodness that macaroons are built upon. Another approach to verification would be to use libmacaroons's built-in verifier to process the macaroon. The verifier hides many of the details of the verification process, and provides a natural way to work with many kinds of caveats. The verifier itself is constructed once, and may be re-used to verify multiple macaroons. >>> V = macaroons.Verifier() >>> V # doctest: +ELLIPSIS Let's go ahead and try to verify the macaroon to see if the request is authorized. To verify the request, we need to provide the verifier with Alice's macaroon, and the secret that was used to construct it. In a real application, we would retrieve the secret using ``M.identifier''; here, we know the secret and provide it directly. A verifier can only ever successfully verify the macaroon when provided with the macaroon and its corresponding secret---no secret, no authorization. Intuitively, our verifier should say that this macaroon is unauthorized because our verifier cannot prove that any of the caveats we've added are satisfied. We can see that it fails just as we would expect: >>> V.verify(M, secret) Traceback (most recent call last): Unauthorized: macaroon not authorized We can inform the verifier of the caveats used by our application using two different techniques. The first technique is to directly provide the verifier with the caveats that match the context of the request. For example, every account-level action in a typical banking application is performed by a specific user and targets a specific account. This information is fixed for each request, and is known at the time of the request. We can tell the verifier directly about these caveats like so: >>> V.satisfy_exact('account = 3735928559') >>> V.satisfy_exact('email = alice@example.org') Caveats like these are called ``exact caveats'' because there is exactly one way to satisfy them. Either the account number is 3735928559, or it isn't. At verification time, the verifier will check each caveat in the macaroon against the list of satisfied caveats provided to ``satisfy_exact''. When it finds a match, it knows that the caveat holds and it can move onto the next caveat in the macaroon. Generally, you will specify multiple true statements as exact caveats, and let the verifier decide which are relevant to each macaroon at verification time. If you provide all exact caveats known to your application to the verifier, it becomes trivial to change policy decisions about authorization. The server performing authorization can treat the verifier as a black-box and does not need to change when changing the authorization policy. The actual policy is enforced when macaroons are minted and when caveats are embedded. In our banking example, we could provide some additional satisfied caveats to the verifier, to describe some (or all) of the properties that are known about the current request. In this manner, the verifier can be made more general, and be "future-proofed", so that it will still function correctly even if somehow the authorization policy for Alice changes; for example, by adding the three following facts, the verifier will continue to work even if Alice decides to self-attenuate her macaroons to be only usable from her IP address and browser: >>> V.satisfy_exact('IP = 127.0.0.1') >>> V.satisfy_exact('browser = Chrome') >>> V.satisfy_exact('action = deposit') Although it's always possible to satisfy a caveat within a macaroon by providing it directly to the verifier, doing so can be quite tedious. Consider the caveat on access time embedded within Alice's macaroon. While an authorization routine could provide the exact caveat ``time < 2020-01-01T00:00'', doing so would require inspecting the macaroon before building the verifier. Just like using MD5 to hash passwords, inspecting a macaroon's structure to build a verifier for it is considered to be very bad practice, and should be violently demonized in Hacker News discussions with vague, slightly inaccurate allusions to pbkdf2. So how can we tell our verifier that the caveat on access time is satisfied? We could provide many exact caveats of the form ``time < YYYY-mm-ddTHH:MM'', but this reeks of inefficiency. The second technique for satisfying caveats provides a more general solution. Called ``general caveats'', the second technique for informing the verifier that a caveat is satisfied allows for expressive caveats. Whereas exact caveats are checked by simple byte-wise equality, general caveats are checked using an application-provided callback that returns true if and only if the caveat is true within the context of the request. There's no limit on the contents of a general caveat, so long as the callback understands how to determine whether it is satisfied. We can verify the time caveat on Alice's macaroon by writing a function that checks the current time against the time specified by the caveat: >>> import datetime >>> def check_time(caveat): ... if not caveat.startswith('time < '): ... return False ... try: ... now = datetime.datetime.now() ... when = datetime.datetime.strptime(caveat[7:], '%Y-%m-%dT%H:%M') ... return now < when ... except: ... return False ... This callback processes all caveats that begin with ``time < '', and returns True if the specified time has not yet passed. We can see that our caveat does indeed return True when the caveat holds, and False otherwise: >>> check_time('time < 2020-01-01T00:00') True >>> check_time('time < 2014-01-01T00:00') False >>> check_time('account = 3735928559') False We can provide the ``check_time'' function directly to the verifier, so that it may check time-based predicates. >>> V.satisfy_general(check_time) It's finally time to verify our macaroon! Now that we've informed the verifier of all the various caveats that our application could embed within a macaroon, we can expect that the verification step will succeed. >>> V.verify(M, secret) True More importantly, the verifier will also work for macaroons we've not yet seen, like one that only permits Alice to deposit into her account: >>> N = M.add_first_party_caveat('action = deposit') >>> V.verify(N, secret) True Equally important is the verifier's ability to reject improper macaroons because they are not authorized. An improper macaroon could have additional caveats not known to the verifier, or have false general caveats. Worse, an unauthorized macaroon could be an attempt by a determined attacker to break into our bank. The verifier we've built will reject all of these scenarios: >>> # Unknown caveat >>> N = M.add_first_party_caveat('OS = Windows XP') >>> V.verify(N, secret) Traceback (most recent call last): Unauthorized: macaroon not authorized >>> # False caveat >>> N = M.add_first_party_caveat('time < 2014-01-01T00:00') >>> V.verify(N, secret) Traceback (most recent call last): Unauthorized: macaroon not authorized >>> # Bad secret >>> V.verify(M, 'this is not the secret we were looking for') Traceback (most recent call last): Unauthorized: macaroon not authorized >>> # Incompetent hackers trying to change the signature >>> N = macaroons.deserialize('MDAxY2xvY2F0aW9uIGh0dHA6Ly9teWJhbmsvCjAwMjZpZGVudGlmaWVyIHdlIHVzZWQgb3VyIHNl\nY3JldCBrZXkKMDAxZGNpZCBhY2NvdW50ID0gMzczNTkyODU1OQowMDIwY2lkIHRpbWUgPCAyMDIw\nLTAxLTAxVDAwOjAwCjAwMjJjaWQgZW1haWwgPSBhbGljZUBleGFtcGxlLm9yZwowMDJmc2lnbmF0\ndXJlID8f19FL+bkC9p/aoMmIecC7GxdOcLVyUnrv6lJMM7NSCg==\n') >>> print N.inspect() location http://mybank/ identifier we used our secret key cid account = 3735928559 cid time < 2020-01-01T00:00 cid email = alice@example.org signature 3f1fd7d14bf9b902f69fdaa0c98879c0bb1b174e70b572527aefea524c33b352 >>> M.signature == N.signature False >>> V.verify(N, secret) Traceback (most recent call last): Unauthorized: macaroon not authorized Using Third-Party Caveats ------------------------- Like first-party caveats, third-party caveats restrict the context in which a macaroon is authorized, but with a different form of restriction. Where a first-party caveat is checked directly within the verifier, a third-party caveat is checked by the third-party, who provides a discharge macaroon to prove that the original third-party caveat is true. The discharge macaroon is recursively inspected by the verifier; if it verifies successfully, the discharge macaroon serves as a proof that the original third-party caveat is satisfied. Of course, nothing stops discharge macaroons from containing embedded first- or third-party caveats for the verifier to consider during verification. Let's rework the above example to provide Alice with access to her account only after she authenticates with a service that is separate from the service processing her banking transactions. As before, we'll start by constructing a new macaroon with the caveat that is limited to Alice's bank account. >>> secret = 'this is a different super-secret key; never use the same secret twice' >>> public = 'we used our other secret key' >>> location = 'http://mybank/' >>> M = macaroons.create(location, secret, public) >>> M = M.add_first_party_caveat('account = 3735928559') >>> print M.inspect() location http://mybank/ identifier we used our other secret key cid account = 3735928559 signature 1434e674ad84fdfdc9bc1aa00785325c8b6d57341fc7ce200ba4680c80786dda So far, so good. Now let's add a third party caveat to this macaroon that requires that Alice authenticate with ``http://auth.mybank/'' before being authorized access to her account. To add a third-party caveat we'll need to specify a location hint, generate a secret, and somehow be able to identify this secret later. Like the location used when creating a macaroon, the location used when adding a third-party caveat is simply a hint to the application that is uninterpreted by libmacaroons. The secret and identity are handled differently than during creation, however, because they need to be known to the third party. To do this, we'll provide the third-party with a randomly generated ``caveat_key'' and the predicate we wish for it to check. In response, it will give us an identifier that we can embed within the macaroon. Later, when we need to construct a discharge macaroon, we can provide it with just this identifier, from which it can recall the key and predicate to check. Here's what this looks like in code: >>> # you'll likely want to use a higher entropy source to generate this key >>> caveat_key = '4; guaranteed random by a fair toss of the dice' >>> predicate = 'user = Alice' >>> # send_to_auth(caveat_key, predicate) >>> # identifier = recv_from_auth() >>> identifier = 'this was how we remind auth of key/pred' >>> M = M.add_third_party_caveat('http://auth.mybank/', caveat_key, identifier) >>> print M.inspect() location http://mybank/ identifier we used our other secret key cid account = 3735928559 cid this was how we remind auth of key/pred vid AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA027FAuBYhtHwJ58FX6UlVNFtFsGxQHS7uD_w_dedwv4Jjw7UorCREw5rXbRqIKhr cl http://auth.mybank/ signature d27db2fd1f22760e4c3dae8137e2d8fc1df6c0741c18aed4b97256bf78d1f55c We now have a macaroon with a third-party caveat. The most interesting thing to note about this macaroon is that it includes no information that reveals the predicate it encodes. The third-party service knows the key and the predicate, and internally associates them with the identifier, but the identifier itself is arbitrary and betrays no information about the predicate to others. The service at ``http://auth.mybank/'' can authenticate that the user is Alice, and provide proof that the caveat is satisfied, without revealing Alice's identity. Other services can verify M and its associated discharge macaroon, without knowing the predicates the third-parties verified. The process for discharging third party caveats starts with the holder of the initial root macaroon, Alice. Alice looks at the macaroon for the list of third party caveat (location, identifier) pairs that must be addressed. >>> M.third_party_caveats() [('http://auth.mybank/', 'this was how we remind auth of key/pred')] In a real application, we'd look at these third party caveats, and contact each location to retrieve the requisite discharge macaroons. We would include the identifier for the caveat in the request itself, so that the server can recall the secret used to create the third-party caveat. The server can then generate and return a new macaroon that discharges the caveat: >>> D = macaroons.create('http://auth.mybank/', caveat_key, identifier) >>> D = D.add_first_party_caveat('time < 2020-01-01T00:00') >>> print D.inspect() location http://auth.mybank/ identifier this was how we remind auth of key/pred cid time < 2020-01-01T00:00 signature 2ed1049876e9d5840950274b579b0770317df54d338d9d3039c7c67d0d91d63c This new macaroon enables the verifier to determine that the third party caveat is satisfied. Our target service added a time-limiting caveat to this macaroon that ensures that this discharge macaroon does not last forever. This ensures that Alice (or, at least someone authenticated as Alice) cannot use the discharge macaroon indefinitely and will eventually have to re-authenticate. Once Alice has both the root macaroon and the discharge macaroon in her possession, she can make the request to the target service. Making a request with discharge macaroons is only slightly more complicated than making requests with a single macaroon. In addition to serializing and transmitting all involved macaroons, there is preparation step that binds the discharge macaroons to the root macaroon. This binding step ensures that the discharge macaroon is useful only when presented alongside the root macaroon. The root macaroon is used to bind the discharge macaroons like this: >>> DP = M.prepare_for_request(D) If we were to look at the signatures on these prepared discharge macaroons, we would see that the binding process has irreversibly altered their signature(s). >>> D.signature '2ed1049876e9d5840950274b579b0770317df54d338d9d3039c7c67d0d91d63c' >>> DP.signature 'd115ef1c133b1126978d5ab27f69d99ba9d0468cd6c1b7e47b8c1c59019cb019' The root macaroon ``M'' and its discharge macaroons ``MS'' are ready for the request. Alice can serialize them all and send them to the bank to prove she is authorized to access her account. The bank can verify them using the same verifier we built before. We provide the discharge macaroons as a third argument to the verify call: >>> V.verify(M, secret, [DP]) True Without the ``prepare_for_request'' call, the verification would fail: >>> V.verify(M, secret, [D]) Traceback (most recent call last): Unauthorized: macaroon not authorized Choosing Secrets ---------------- For clarity, we've generated human-readable secrets that we use as the root keys of all of our macaroons. In practice, this is terribly insecure and can lead to macaroons that can easily be forged because the secret is too predictable. To avoid this, we recommend generating secrets using a sufficient number of suitably random bytes. Because the bytes are a secret key, they should be drawn from a source with enough entropy to ensure that the key cannot be guessed before the macaroon falls out of use. The macaroons module exposes a constant that is the ideal number of bytes these secret keys should contain. Any shorter is wasting an opportunity for security. >>> macaroons.SUGGESTED_SECRET_LENGTH 32 To generate a suitable key is very simple using the python standard library: >>> import os >>> import binascii >>> binascii.hexlify(os.urandom(macaroons.SUGGESTED_SECRET_LENGTH)) # doctest: +ELLIPSIS '...' Which gives a long string of hex bytes which can be passed into the create and validate functions as-is. Third-Party Caveats with Public Keys ------------------------------------ Public key cryptography can enable much more efficient schemes for adding third-party caveats. In the above example where we added a third-party caveat, the caveat's identifier was generated by the third party and retrieved with in one round trip. We can eliminate the round trip when the third party has a well-known public key. We can encrypt the caveat's secret, and the predicate to be checked using this public key, and use the ciphertext directly as the caveat's identifier. This saves a round trip, and frees the third party from having to remember an association between identifiers and key/predicate pairs. [1] http://research.google.com/pubs/pub41892.html [2] https://github.com/jedisct1/libsodium libmacaroons-releases-0.3.0/base64.c000066400000000000000000000220371253111166600172220ustar00rootroot00000000000000/* $OpenBSD: base64.c,v 1.7 2013/12/31 02:32:56 tedu Exp $ */ /* * Copyright (c) 1996 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) grants * permission under its copyrights to use, copy, modify, and distribute this * Software with or without fee, provided that the above copyright notice and * all paragraphs of this notice appear in all copies, and that the name of IBM * not be used in connection with the marketing of any product incorporating * the Software or modifications thereof, without specific, written prior * permission. * * To the extent it has a right to do so, IBM grants an immunity from suit * under its patents, if any, for the use, sale or manufacture of products to * the extent that such products are used for performing Domain Name System * dynamic updates in TCP/IP networks by means of the Software. No immunity is * granted for any product per se or for any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* c */ #include #include #include /* macaroons */ #include "base64.h" static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; static const char Pad64 = '='; /* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) The following encoding technique is taken from RFC 1521 by Borenstein and Freed. It is reproduced here in a slightly edited form for convenience. A 65-character subset of US-ASCII is used, enabling 6 bits to be represented per printable character. (The extra 65th character, "=", is used to signify a special processing function.) The encoding process represents 24-bit groups of input bits as output strings of 4 encoded characters. Proceeding from left to right, a 24-bit input group is formed by concatenating 3 8-bit input groups. These 24 bits are then treated as 4 concatenated 6-bit groups, each of which is translated into a single digit in the base64 alphabet. Each 6-bit group is used as an index into an array of 64 printable characters. The character referenced by the index is placed in the output string. Table 1: The Base64 Alphabet Value Encoding Value Encoding Value Encoding Value Encoding 0 A 17 R 34 i 51 z 1 B 18 S 35 j 52 0 2 C 19 T 36 k 53 1 3 D 20 U 37 l 54 2 4 E 21 V 38 m 55 3 5 F 22 W 39 n 56 4 6 G 23 X 40 o 57 5 7 H 24 Y 41 p 58 6 8 I 25 Z 42 q 59 7 9 J 26 a 43 r 60 8 10 K 27 b 44 s 61 9 11 L 28 c 45 t 62 + 12 M 29 d 46 u 63 / 13 N 30 e 47 v 14 O 31 f 48 w (pad) = 15 P 32 g 49 x 16 Q 33 h 50 y Special processing is performed if fewer than 24 bits are available at the end of the data being encoded. A full encoding quantum is always completed at the end of a quantity. When fewer than 24 input bits are available in an input group, zero bits are added (on the right) to form an integral number of 6-bit groups. Padding at the end of the data is performed using the '=' character. Since all base64 input is an integral number of octets, only the ------------------------------------------------- following cases can arise: (1) the final quantum of encoding input is an integral multiple of 24 bits; here, the final unit of encoded output will be an integral multiple of 4 characters with no "=" padding, (2) the final quantum of encoding input is exactly 8 bits; here, the final unit of encoded output will be two characters followed by two "=" padding characters, or (3) the final quantum of encoding input is exactly 16 bits; here, the final unit of encoded output will be three characters followed by one "=" padding character. */ int b64_ntop(src, srclength, target, targsize) unsigned char const *src; size_t srclength; char *target; size_t targsize; { size_t datalength = 0; unsigned char input[3]; unsigned char output[4]; size_t i; while (2 < srclength) { input[0] = *src++; input[1] = *src++; input[2] = *src++; srclength -= 3; output[0] = input[0] >> 2; output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); output[3] = input[2] & 0x3f; if (datalength + 4 > targsize) return (-1); target[datalength++] = Base64[output[0]]; target[datalength++] = Base64[output[1]]; target[datalength++] = Base64[output[2]]; target[datalength++] = Base64[output[3]]; } /* Now we worry about padding. */ if (0 != srclength) { /* Get what's left. */ input[0] = input[1] = input[2] = '\0'; for (i = 0; i < srclength; i++) input[i] = *src++; output[0] = input[0] >> 2; output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); if (datalength + 4 > targsize) return (-1); target[datalength++] = Base64[output[0]]; target[datalength++] = Base64[output[1]]; if (srclength != 1) target[datalength++] = Base64[output[2]]; } if (datalength >= targsize) return (-1); target[datalength] = '\0'; /* Returned value doesn't count \0. */ return (datalength); } /* skips all whitespace anywhere. converts characters, four at a time, starting at (or after) src from base - 64 numbers into three 8 bit bytes in the target area. it returns the number of data bytes stored at the target, or -1 on error. */ int b64_pton(src, target, targsize) char const *src; unsigned char *target; size_t targsize; { size_t tarindex; int state, ch; unsigned char nextbyte; char *pos; state = 0; tarindex = 0; while ((ch = (unsigned char)*src++) != '\0') { if (isspace(ch)) /* Skip whitespace anywhere. */ continue; if (ch == Pad64) break; if (ch == '+') ch = '-'; if (ch == '/') ch = '_'; pos = strchr(Base64, ch); if (pos == 0) /* A non-base64 character. */ return (-1); switch (state) { case 0: if (target) { if (tarindex >= targsize) return (-1); target[tarindex] = (pos - Base64) << 2; } state = 1; break; case 1: if (target) { if (tarindex >= targsize) return (-1); target[tarindex] |= (pos - Base64) >> 4; nextbyte = ((pos - Base64) & 0x0f) << 4; if (tarindex + 1 < targsize) target[tarindex+1] = nextbyte; else if (nextbyte) return (-1); } tarindex++; state = 2; break; case 2: if (target) { if (tarindex >= targsize) return (-1); target[tarindex] |= (pos - Base64) >> 2; nextbyte = ((pos - Base64) & 0x03) << 6; if (tarindex + 1 < targsize) target[tarindex+1] = nextbyte; else if (nextbyte) return (-1); } tarindex++; state = 3; break; case 3: if (target) { if (tarindex >= targsize) return (-1); target[tarindex] |= (pos - Base64); } tarindex++; state = 0; break; default: break; } } /* Skip padding and whitespace */ if (ch == Pad64) { while (*src != '\0') { if (!isspace(*src) && *src != Pad64) { return (-1); } ++src; } } if (target && tarindex < targsize && target[tarindex] != 0 && state != 0) { return (-1); } return (tarindex); } libmacaroons-releases-0.3.0/base64.h000066400000000000000000000035071253111166600172300ustar00rootroot00000000000000/* Copyright (c) 2014, 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. */ #ifndef macaroons_base64_h_ #define macaroons_base64_h_ int b64_ntop(const unsigned char* src, size_t srclength, char* target, size_t targsize); int b64_pton(const char* src, unsigned char* target, size_t targsize); #endif /* macaroons_base64_h_ */ libmacaroons-releases-0.3.0/bindings/000077500000000000000000000000001253111166600175635ustar00rootroot00000000000000libmacaroons-releases-0.3.0/bindings/go/000077500000000000000000000000001253111166600201705ustar00rootroot00000000000000libmacaroons-releases-0.3.0/bindings/go/macaroons/000077500000000000000000000000001253111166600221525ustar00rootroot00000000000000libmacaroons-releases-0.3.0/bindings/go/macaroons/README000066400000000000000000000011601253111166600230300ustar00rootroot00000000000000Go bindings for libmacaroons ============================ Build ----- Assumes libsodium headers in your system include path. libmacaroons headers are included from a relative path in the source distribution. $ go build . Test ---- Dependencies ~~~~~~~~~~~~ Requires libsodium.so.4 and libmacaroons.so.0 in your LD_LIBRARY_PATH. With libsodium in your system library path, and libmacaroons compiled in this source tree, you can set: $ export LD_LIBRARY_PATH=../../../.libs Requires Gustavo Niemeyer's gocheck package. Install into your GOPATH with: $ go get gopkg.in/check.v1 Now you're ready to test: $ go test . libmacaroons-releases-0.3.0/bindings/go/macaroons/cutil.go000066400000000000000000000023031253111166600236170ustar00rootroot00000000000000// Copyright 2013 The Go-SQLite Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Adapted from github.com/mxk/go-sqlite/sqlite3/util.go by // Casey Marshall package macaroons import "C" import ( "reflect" "unsafe" ) const nuls = "\x00" // cStr returns a char* pointer to the first byte in s. func cStr(s string) *C.char { h := (*reflect.StringHeader)(unsafe.Pointer(&s)) return (*C.char)(unsafe.Pointer(h.Data)) } // cUStrN returns an unsigned char* pointer to the first byte in s and the byte // length of the string. func cUStrN(s string) (*C.uchar, C.size_t) { h := (*reflect.StringHeader)(unsafe.Pointer(&s)) return (*C.uchar)(unsafe.Pointer(h.Data)), C.size_t(len(s)) } // cBytes returns a pointer to the first byte in b. func cBytes(b []byte) *C.char { return (*C.char)(unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&b)).Data)) } // goUStrN returns a Go representation of an n-unsigned byte C string. func goStrN(p *C.uchar, n C.size_t) (s string) { if n > 0 { h := (*reflect.StringHeader)(unsafe.Pointer(&s)) h.Data = uintptr(unsafe.Pointer(p)) h.Len = int(n) } return } libmacaroons-releases-0.3.0/bindings/go/macaroons/macaroons.go000066400000000000000000000254001253111166600244640ustar00rootroot00000000000000/* Copyright (c) 2014, Casey Marshall * 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. */ // Package macaroons implements a Go binding for libmacaroons. // // A pure Go package with equivalent (and compatible) functionality also // exists. See http://godoc.org/gopkg.in/macaroon.v1. package macaroons /* #cgo CFLAGS: -I../../.. #cgo LDFLAGS: -L../../../.libs -lmacaroons -lsodium #include #include #include "macaroons.h" */ import "C" import ( "bytes" "encoding/base64" "fmt" "unsafe" ) // macaroonError returns an error describing the macaroon return code. func macaroonError(err C.enum_macaroon_returncode) error { switch err { case C.MACAROON_SUCCESS: return nil case C.MACAROON_OUT_OF_MEMORY: return fmt.Errorf("out of memory") case C.MACAROON_HASH_FAILED: return fmt.Errorf("hash failed") case C.MACAROON_INVALID: return fmt.Errorf("invalid") case C.MACAROON_TOO_MANY_CAVEATS: return fmt.Errorf("too many caveats") case C.MACAROON_CYCLE: return fmt.Errorf("cycle") case C.MACAROON_BUF_TOO_SMALL: return fmt.Errorf("buffer too small") case C.MACAROON_NOT_AUTHORIZED: return fmt.Errorf("not authorized") case C.MACAROON_NO_JSON_SUPPORT: return fmt.Errorf("no JSON support") } return fmt.Errorf("unknown error %d", err) } // Macaroon holds a macaroon. // See Fig. 7 of http://theory.stanford.edu/~ataly/Papers/macaroons.pdf // for a description of the data contained within. // Macaroons are mutable objects - use Copy as appropriate // to avoid unwanted mutation. type Macaroon struct { m *C.struct_macaroon } type ThirdPartyId struct { Location, Id string } // NewMacaroon returns a new macaroon with the given location, // root key and identifier. func NewMacaroon(location, key, id string) (*Macaroon, error) { var err C.enum_macaroon_returncode cLoc, cLocSz := cUStrN(location) cKey, cKeySz := cUStrN(key) cId, cIdSz := cUStrN(id) m := C.macaroon_create(cLoc, cLocSz, cKey, cKeySz, cId, cIdSz, &err) if err != 0 { defer C.macaroon_destroy(m) return nil, macaroonError(err) } return &Macaroon{m}, nil } // Destroy frees the memory held by a macaroon. func (m *Macaroon) Destroy() { C.macaroon_destroy(m.m) m.m = nil } // Validate does nothing. func (m *Macaroon) Validate() error { rc := C.macaroon_validate(m.m) if rc != 0 { return fmt.Errorf("validation error: %d", rc) } return nil } func (m *Macaroon) newFirstPartyCaveat(predicate string) (*Macaroon, error) { var err C.enum_macaroon_returncode cPred, cPredSz := cUStrN(predicate) mPrime := C.macaroon_add_first_party_caveat(m.m, cPred, cPredSz, &err) if err != 0 { return nil, macaroonError(err) } return &Macaroon{mPrime}, nil } // WithFirstPartyCaveat adds a first party caveat with the given predicate // to m. func (m *Macaroon) WithFirstPartyCaveat(predicate string) error { mNext, err := m.newFirstPartyCaveat(predicate) if err != nil { return err } mPrev := m.m m.m = mNext.m C.macaroon_destroy(mPrev) return nil } func (m *Macaroon) newThirdPartyCaveat(location, key, id string) (*Macaroon, error) { var err C.enum_macaroon_returncode cLoc, cLocSz := cUStrN(location) cKey, cKeySz := cUStrN(key) cId, cIdSz := cUStrN(id) mNew := C.macaroon_add_third_party_caveat(m.m, cLoc, cLocSz, cKey, cKeySz, cId, cIdSz, &err) if err != 0 { return nil, macaroonError(err) } return &Macaroon{mNew}, nil } // WithThirdPartyCaveat adds a caveat with the given location, // key and id to m. func (m *Macaroon) WithThirdPartyCaveat(location, key, id string) error { mNext, err := m.newThirdPartyCaveat(location, key, id) if err != nil { return err } mPrev := m.m m.m = mNext.m C.macaroon_destroy(mPrev) return nil } // Marshal returns the macaroon marshaled as for // m.MarshalBinary, but also encapsulated in URL-safe // base64 with no padding. func (m *Macaroon) Marshal() (string, error) { var err C.enum_macaroon_returncode n := C.macaroon_serialize_size_hint(m.m) buf := make([]byte, n) data := cBytes(buf) sz := C.macaroon_serialize(m.m, data, n, &err) if sz < 0 { return "", macaroonError(err) } buf = bytes.TrimRight(buf, nuls) return string(buf), nil } // Unmarshal unmarshals a macaroon in the format produced // by Marshal. It also accepts base64-encoded JSON format. func Unmarshal(s string) (*Macaroon, error) { var err C.enum_macaroon_returncode data := cStr(s) m := C.macaroon_deserialize(data, &err) if m == nil { // TODO: err gets set to INVALID even if this returns successful, fix that return nil, macaroonError(err) } return &Macaroon{m}, nil } // Location returns macaroon's location. func (m *Macaroon) Location() string { var loc *C.uchar var locSz C.size_t C.macaroon_location(m.m, &loc, &locSz) return goStrN(loc, locSz) } // Id returns the macaroon's identifier. func (m *Macaroon) Id() string { var id *C.uchar var idSz C.size_t C.macaroon_identifier(m.m, &id, &idSz) return goStrN(id, idSz) } // Signature returns the macaroon's signature. // Note that the string is a binary bit string. func (m *Macaroon) Signature() string { var sig *C.uchar var sigSz C.size_t C.macaroon_signature(m.m, &sig, &sigSz) return goStrN(sig, sigSz) } // Inspect returns the macaroon in "human readable" // format. func (m *Macaroon) Inspect() (string, error) { var err C.enum_macaroon_returncode n := C.macaroon_inspect_size_hint(m.m) buf := make([]byte, n) data := cBytes(buf) sz := C.macaroon_inspect(m.m, data, n, &err) if sz < 0 { return "", macaroonError(err) } else if sz < 0 { return "", fmt.Errorf("serialization error") } buf = bytes.TrimRight(buf, nuls) return string(buf), nil } // ThirdPartyCaveats returns all the third party caveats // in m. func (m *Macaroon) ThirdPartyCaveats() ([]ThirdPartyId, error) { var result []ThirdPartyId n := C.macaroon_num_third_party_caveats(m.m) for i := C.uint(0); i < n; i++ { var loc *C.uchar var locSz C.size_t var id *C.uchar var idSz C.size_t rc := C.macaroon_third_party_caveat(m.m, i, &loc, &locSz, &id, &idSz) if rc < 0 { return nil, fmt.Errorf("failed to read third-party caveat %d", i) } result = append(result, ThirdPartyId{ Location: goStrN(loc, locSz), Id: goStrN(id, idSz), }) } return result, nil } // PrepareForRequest returns a copy of the given discharge macaroon // bound to the primary macaroon m. func (m *Macaroon) PrepareForRequest(discharge *Macaroon) (*Macaroon, error) { var err C.enum_macaroon_returncode prepared := C.macaroon_prepare_for_request(m.m, discharge.m, &err) if prepared == nil { return nil, macaroonError(err) } return &Macaroon{prepared}, nil } // Copy returns a copy of the macaroon. func (m *Macaroon) Copy() (*Macaroon, error) { var err C.enum_macaroon_returncode newM := C.macaroon_copy(m.m, &err) if newM == nil { return nil, macaroonError(err) } return &Macaroon{newM}, nil } func cbuf(data []byte) *C.char { if len(data) == 0 { return nil } return (*C.char)(unsafe.Pointer(&data[0])) } // MarshalJSON marshals the macaroon to JSON format. // It implements json.Marshaler. func (m *Macaroon) MarshalJSON() ([]byte, error) { var merr C.enum_macaroon_returncode b64data := make([]byte, C.macaroon_serialize_json_size_hint(m.m)) rc := C.macaroon_serialize_json(m.m, cbuf(b64data), C.size_t(len(b64data)), &merr) if rc < 0 { return nil, macaroonError(merr) } return base64Decode(b64data) } // UnmarshalJSON unmarshals the macaroon from JSON format. // It implements json.Unmarshaler. func (m *Macaroon) UnmarshalJSON(data []byte) error { var err C.enum_macaroon_returncode cm := C.macaroon_deserialize_json(cbuf(data), C.size_t(len(data)), &err) if cm == nil { return macaroonError(err) } m.m = cm return nil } // MarshalBinary marshals the macaroon into binary format. // This is the same as Marshal but without the base64 encoding. func (m *Macaroon) MarshalBinary() ([]byte, error) { var merr C.enum_macaroon_returncode b64data := make([]byte, C.macaroon_serialize_size_hint(m.m)) rc := C.macaroon_serialize(m.m, cbuf(b64data), C.size_t(len(b64data)), &merr) if rc < 0 { return nil, macaroonError(merr) } return base64Decode(b64data) } // UnmarshalBinary unmarshals the macaroon from binary // format. func (m *Macaroon) UnmarshalBinary(data []byte) error { // libmacaroons expects the serialization in base64 encoding already, // but we just have the binary, so encode it for libmacaroons. b64data := make([]byte, base64.URLEncoding.EncodedLen(len(data))) base64.URLEncoding.Encode(b64data, data) var err C.enum_macaroon_returncode cm := C.macaroon_deserialize(cbuf(b64data), &err) if cm == nil { return macaroonError(err) } m.m = cm return nil } // Cmp returns 0 if the macaroons are equal, and // non-zero otherwise. func Cmp(a, b *Macaroon) int { if a == nil || a.m == nil || b == nil || b.m == nil { panic("compare nil macaroon") } return int(C.macaroon_cmp(a.m, b.m)) } // base64Decode decodes base64 data that might be missing trailing // pad characters. func base64Decode(b64data []byte) ([]byte, error) { if i := bytes.IndexByte(b64data, 0); i >= 0 { b64data = b64data[0:i] } paddedLen := (len(b64data) + 3) / 4 * 4 for i := len(b64data); i < paddedLen; i++ { b64data = append(b64data, '=') } data := make([]byte, base64.URLEncoding.DecodedLen(len(b64data))) n, err := base64.URLEncoding.Decode(data, b64data) if err != nil { return nil, fmt.Errorf("cannot decode base64 macaroon serialization: %v", b64data, err) } return data[0:n], nil } libmacaroons-releases-0.3.0/bindings/go/macaroons/macaroons_test.go000066400000000000000000000110671253111166600255270ustar00rootroot00000000000000/* Copyright (c) 2014, Casey Marshall * 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. */ package macaroons import ( gc "gopkg.in/check.v1" ) func (s *Suite) TestHelloMacaroons(c *gc.C) { m, err := NewMacaroon("test", "hunter2", "AzureDiamond") c.Assert(err, gc.IsNil) defer m.Destroy() c.Assert(m, gc.NotNil) c.Check(m.Location(), gc.Equals, "test") c.Check(m.Id(), gc.Equals, "AzureDiamond") err = m.WithFirstPartyCaveat("hello = world") c.Assert(err, gc.IsNil) err = m.Validate() c.Assert(err, gc.IsNil) out, err := m.Marshal() c.Assert(err, gc.IsNil) m2, err := Unmarshal(out) c.Assert(err, gc.IsNil) defer m2.Destroy() c.Check(m2.Signature(), gc.Equals, m.Signature()) c.Check(m2.Location(), gc.Equals, "test") c.Check(m2.Id(), gc.Equals, "AzureDiamond") c.Check(Cmp(m, m2), gc.Equals, 0) err = m2.Validate() c.Assert(err, gc.IsNil) m3, err := m2.Copy() c.Assert(err, gc.IsNil) defer m3.Destroy() c.Check(m3.Signature(), gc.Equals, m.Signature()) c.Check(m3.Location(), gc.Equals, "test") c.Check(m3.Id(), gc.Equals, "AzureDiamond") c.Check(Cmp(m, m3), gc.Equals, 0) } func (s *Suite) TestCaveatsChangeThings(c *gc.C) { m, err := NewMacaroon("pandora", "catch a ride", "scooter") c.Assert(err, gc.IsNil) defer m.Destroy() var last string for _, predicate := range []string{"roland", "mordecai", "lilith", "brick"} { err = m.WithFirstPartyCaveat(predicate) c.Assert(err, gc.IsNil) next, err := m.Marshal() c.Assert(err, gc.IsNil) c.Assert(next, gc.Not(gc.HasLen), 0) c.Assert(last, gc.Not(gc.Equals), next) last = next } for _, tp := range []struct { loc, key, id string }{ {"axton", "commando", "turret"}, {"maya", "siren", "phaselock"}, {"salvador", "gunzerker", "dual-wield"}, {"zero", "a number", "hologram"}, } { err = m.WithThirdPartyCaveat(tp.loc, tp.key, tp.id) c.Assert(err, gc.IsNil) next, err := m.Marshal() c.Assert(err, gc.IsNil) c.Assert(next, gc.Not(gc.Equals), "") c.Assert(last, gc.Not(gc.Equals), next) last = next } tps, err := m.ThirdPartyCaveats() c.Assert(err, gc.IsNil) c.Assert(tps, gc.HasLen, 4) c.Check(tps[0], gc.DeepEquals, ThirdPartyId{Location: "axton", Id: "turret"}) c.Check(tps[1], gc.DeepEquals, ThirdPartyId{Location: "maya", Id: "phaselock"}) c.Check(tps[2], gc.DeepEquals, ThirdPartyId{Location: "salvador", Id: "dual-wield"}) c.Check(tps[3], gc.DeepEquals, ThirdPartyId{Location: "zero", Id: "hologram"}) } func (s *Suite) TestInspect(c *gc.C) { m, err := NewMacaroon("ingsoc", "under the spreading chestnut tree", "wsmith") c.Assert(err, gc.IsNil) defer m.Destroy() desc, err := m.Inspect() c.Assert(err, gc.IsNil) c.Check(desc, gc.Equals, `location ingsoc identifier wsmith signature d5c974d83f28c451f7955af20fd13c97296f0344f762bf7b89d91b31f2abdb30`) m.WithFirstPartyCaveat("war = peace") m.WithFirstPartyCaveat("slavery = freedom") m.WithFirstPartyCaveat("ignorance = strength") desc, err = m.Inspect() c.Check(desc, gc.Equals, `location ingsoc identifier wsmith cid war = peace cid slavery = freedom cid ignorance = strength signature ef6e75301b1cafde6e87a1d44adab81b6492f5c11d51026c4a5be9beda1a07be`) } libmacaroons-releases-0.3.0/bindings/go/macaroons/suite_test.go000066400000000000000000000033251253111166600246740ustar00rootroot00000000000000/* Copyright (c) 2014, Casey Marshall * 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. */ package macaroons import ( "testing" gc "gopkg.in/check.v1" ) func Test(t *testing.T) { gc.TestingT(t) } type Suite struct{} var _ = gc.Suite(&Suite{}) libmacaroons-releases-0.3.0/bindings/go/macaroons/verifier.go000066400000000000000000000066121253111166600243210ustar00rootroot00000000000000/* Copyright (c) 2014, Casey Marshall * 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. */ package macaroons /* #cgo CFLAGS: -I../../.. #cgo LDFLAGS: -L../../../.libs -lmacaroons -lsodium #include #include #include "macaroons.h" #include "wrapper.h" */ import "C" import ( "fmt" "reflect" "unsafe" ) type Verifier struct { v *C.struct_macaroon_verifier callbacks []*GeneralCaveat } func NewVerifier() *Verifier { return &Verifier{v: C.macaroon_verifier_create()} } func (v *Verifier) Destroy() { C.macaroon_verifier_destroy(v.v) } func (v *Verifier) SatisfyExact(predicate string) error { var err C.enum_macaroon_returncode pred, predSz := cUStrN(predicate) rc := C.macaroon_verifier_satisfy_exact(v.v, pred, predSz, &err) if rc < 0 { return macaroonError(err) } return nil } type GeneralCaveat func(s string) bool //export goGeneralCheck func goGeneralCheck(f unsafe.Pointer, pred *C.uchar, predSz C.size_t) C.int { caveat := (*GeneralCaveat)(unsafe.Pointer(f)) if (*caveat)(goStrN(pred, predSz)) { return 0 } return -1 } func (v *Verifier) SatisfyGeneral(caveat GeneralCaveat) error { var err C.enum_macaroon_returncode // Prevent the closure from garbage collection. v.callbacks = append(v.callbacks, &caveat) rc := C.addSatisfier(v.v, unsafe.Pointer(&caveat), &err) if rc < 0 { return macaroonError(err) } return nil } func (v *Verifier) Verify(m *Macaroon, key string, discharges ...*Macaroon) error { var err C.enum_macaroon_returncode msLen := C.size_t(len(discharges)) ms := make([]*C.struct_macaroon, msLen) for i := range discharges { ms[i] = discharges[i].m } msPtr := (**C.struct_macaroon)(unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&ms)).Data)) keyPtr, keySz := cUStrN(key) rc := C.macaroon_verify(v.v, m.m, keyPtr, keySz, msPtr, msLen, &err) if rc == 0 { return nil } if err != 0 { return macaroonError(err) } return fmt.Errorf("verify error: %d", rc) } libmacaroons-releases-0.3.0/bindings/go/macaroons/verifier_test.go000066400000000000000000000136061253111166600253610ustar00rootroot00000000000000/* Copyright (c) 2014, Casey Marshall * 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. */ package macaroons import ( "strings" "time" gc "gopkg.in/check.v1" ) func (s *Suite) TestVerifyExact(c *gc.C) { usernameRule := "username = tk421" locationRule := "location = cargo-bay-11" secret := "not a moon" m, err := NewMacaroon("death star", secret, "tk421@deathstar") c.Assert(err, gc.IsNil) defer m.Destroy() c.Assert(m, gc.NotNil) // no caveats, ok func() { v1 := NewVerifier() defer v1.Destroy() err = v1.Verify(m, secret) c.Assert(err, gc.IsNil) }() // wrong secret, err func() { v2 := NewVerifier() defer v2.Destroy() err = v2.Verify(m, "han shot first") c.Assert(err, gc.NotNil) }() // fail username caveat, err func() { m1, err := m.Copy() c.Assert(err, gc.IsNil) defer m1.Destroy() m1.WithFirstPartyCaveat("username = r2d2") v := NewVerifier() defer v.Destroy() v.SatisfyExact(usernameRule) v.SatisfyExact(locationRule) err = v.Verify(m1, secret) c.Assert(err, gc.NotNil, gc.Commentf("%+v", err)) // TK-421 is not at his post }() // fail location caveat, err func() { m2, err := m.Copy() c.Assert(err, gc.IsNil) defer m2.Destroy() m2.WithFirstPartyCaveat("username = tk421") m2.WithFirstPartyCaveat("location = detention-level-aa23") v := NewVerifier() v.SatisfyExact(usernameRule) v.SatisfyExact(locationRule) err = v.Verify(m2, secret) c.Assert(err, gc.NotNil) // TK-421 is not at his post }() // satisfy both caveats, ok func() { m3, err := m.Copy() c.Assert(err, gc.IsNil) defer m3.Destroy() m3.WithFirstPartyCaveat("username = tk421") m3.WithFirstPartyCaveat("location = cargo-bay-11") v := NewVerifier() v.SatisfyExact(usernameRule) v.SatisfyExact(locationRule) err = v.Verify(m3, secret) c.Assert(err, gc.IsNil, gc.Commentf("%+v", err)) // TK-421 is at his post }() } const timeLayout = "2006-01-02T15:04:05 -0700" func checkTimeAt(nowString string) GeneralCaveat { now, err := time.Parse(timeLayout, nowString) if err != nil { panic(err) } return func(s string) bool { fields := strings.SplitN(s, " ", 3) if len(fields) != 3 { return false } if fields[0] != "time" { return false } if fields[1] != "<" { return false } deadline, err := time.Parse(timeLayout, fields[2]) if err != nil { return false } return now.Before(deadline) } } func (s *Suite) TestVerifyGeneral(c *gc.C) { secret := "wait til you see those goddamn bats" m, err := NewMacaroon("The Mint Hotel", secret, "hst") c.Assert(err, gc.IsNil) defer m.Destroy() c.Assert(m, gc.NotNil) deadline := "time < 1971-11-11T16:00:00 -0800" func() { v := NewVerifier() err = v.SatisfyGeneral(checkTimeAt("2014-05-08T23:40:00 +0000")) c.Assert(err, gc.IsNil) m2, err := m.Copy() c.Assert(err, gc.IsNil) err = m2.WithFirstPartyCaveat(deadline) c.Assert(err, gc.IsNil) err = v.Verify(m2, secret) c.Assert(err, gc.NotNil) }() func() { v := NewVerifier() err = v.SatisfyGeneral(checkTimeAt("1971-11-11T15:59:59 -0800")) c.Assert(err, gc.IsNil) m2, err := m.Copy() c.Assert(err, gc.IsNil) err = m2.WithFirstPartyCaveat(deadline) c.Assert(err, gc.IsNil) err = v.Verify(m2, secret) c.Assert(err, gc.IsNil) }() } func (s *Suite) TestVerifyThirdParty(c *gc.C) { secret := "this is a different super-secret key; never use the same secret twice" public := "we used our other secret key" location := "http://mybank/" m, err := NewMacaroon(location, secret, public) c.Assert(err, gc.IsNil) defer m.Destroy() c.Assert(m, gc.NotNil) err = m.WithFirstPartyCaveat("account = 3735928559") c.Assert(err, gc.IsNil) caveatKey := "4; guaranteed random by a fair toss of the dice" identifier := "this was how we remind auth of key/pred" err = m.WithThirdPartyCaveat("http://auth.mybank/", caveatKey, identifier) c.Assert(err, gc.IsNil) discharge, err := NewMacaroon("http://auth.mybank/", caveatKey, identifier) c.Assert(err, gc.IsNil) defer discharge.Destroy() err = discharge.WithFirstPartyCaveat("time < 2015-01-01T00:00:00 +0000") c.Assert(err, gc.IsNil) preparedDischarge, err := m.PrepareForRequest(discharge) c.Assert(err, gc.IsNil) defer preparedDischarge.Destroy() v := NewVerifier() defer v.Destroy() err = v.SatisfyExact("account = 3735928559") c.Assert(err, gc.IsNil) err = v.SatisfyGeneral(checkTimeAt("2014-05-30T20:25:00 -0500")) c.Assert(err, gc.IsNil) err = v.Verify(m, secret, preparedDischarge) c.Assert(err, gc.IsNil) } libmacaroons-releases-0.3.0/bindings/go/macaroons/wrapper.c000066400000000000000000000036471253111166600240100ustar00rootroot00000000000000/* Copyright (c) 2014, Casey Marshall * 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. */ #include #include #include "macaroons.h" #include "wrapper.h" extern int goGeneralCheck(void *f, unsigned char *pred, size_t pred_sz); int addSatisfier(struct macaroon_verifier *v, void *arg, enum macaroon_returncode *err) { return macaroon_verifier_satisfy_general(v, (int (*)(void *, const unsigned char *, size_t))goGeneralCheck, arg, err); } libmacaroons-releases-0.3.0/bindings/go/macaroons/wrapper.h000066400000000000000000000034061253111166600240060ustar00rootroot00000000000000/* Copyright (c) 2014, Casey Marshall * 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. */ #ifndef _WRAPPER_H_ #define _WRAPPER_H_ #include #include #include "macaroons.h" extern int addSatisfier(struct macaroon_verifier *v, void *arg, enum macaroon_returncode *err); #endif libmacaroons-releases-0.3.0/bindings/python/000077500000000000000000000000001253111166600211045ustar00rootroot00000000000000libmacaroons-releases-0.3.0/bindings/python/macaroons.pyx000066400000000000000000000320771253111166600236410ustar00rootroot00000000000000# Copyright (c) 2014, 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. cdef extern from "stdlib.h": void* malloc(size_t size) void free(void* ptr) cdef extern from "macaroons.h": DEF MACAROON_MAX_STRLEN = 32768 DEF MACAROON_MAX_CAVEATS = 65536 cdef struct macaroon cdef struct macaroon_verifier cdef enum macaroon_returncode: MACAROON_SUCCESS = 2048 MACAROON_OUT_OF_MEMORY = 2049 MACAROON_HASH_FAILED = 2050 MACAROON_INVALID = 2051 MACAROON_TOO_MANY_CAVEATS = 2052 MACAROON_CYCLE = 2053 MACAROON_BUF_TOO_SMALL = 2054 MACAROON_NOT_AUTHORIZED = 2055 MACAROON_NO_JSON_SUPPORT = 2056 macaroon* macaroon_create(unsigned char* location, size_t location_sz, unsigned char* key, size_t key_sz, unsigned char* id, size_t id_sz, macaroon_returncode* err) void macaroon_destroy(macaroon* M) int macaroon_validate(const macaroon* M) macaroon* macaroon_add_first_party_caveat(const macaroon* M, const unsigned char* predicate, size_t predicate_sz, macaroon_returncode* err) macaroon* macaroon_add_third_party_caveat(const macaroon* M, const unsigned char* location, size_t location_sz, const unsigned char* key, size_t key_sz, const unsigned char* id, size_t id_sz, macaroon_returncode* err) unsigned macaroon_num_third_party_caveats(const macaroon* M) int macaroon_third_party_caveat(const macaroon* M, unsigned which, const unsigned char** location, size_t* location_sz, const unsigned char** identifier, size_t* identifier_sz) macaroon* macaroon_prepare_for_request(const macaroon* M, const macaroon* D, macaroon_returncode* err) macaroon_verifier* macaroon_verifier_create() void macaroon_verifier_destroy(macaroon_verifier* V) int macaroon_verifier_satisfy_exact(macaroon_verifier* V, const unsigned char* predicate, size_t predicate_sz, macaroon_returncode* err) int macaroon_verifier_satisfy_general(macaroon_verifier* V, int (*general_check)(void* f, const unsigned char* pred, size_t pred_sz), void* f, macaroon_returncode* err) int macaroon_verify(const macaroon_verifier* V, const macaroon* M, const unsigned char* key, size_t key_sz, macaroon** MS, size_t MS_sz, macaroon_returncode* err) 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) size_t macaroon_serialize_json_size_hint(const macaroon* M) int macaroon_serialize_json(const macaroon* M, char* data, size_t data_sz, macaroon_returncode* err) macaroon* macaroon_deserialize(char* data, 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) SUGGESTED_SECRET_LENGTH = 32 class MacaroonError(Exception): pass class Unauthorized(Exception): pass cdef raise_error(macaroon_returncode err): if err == MACAROON_OUT_OF_MEMORY: raise MemoryError X = {MACAROON_HASH_FAILED: 'HMAC function failed', MACAROON_INVALID: 'macaroon invalid', MACAROON_TOO_MANY_CAVEATS: 'too many caveats', MACAROON_CYCLE: 'discharge caveats form a cycle', MACAROON_BUF_TOO_SMALL: 'buffer too small', MACAROON_NOT_AUTHORIZED: 'not authorized', MACAROON_NO_JSON_SUPPORT: 'JSON macaroons not supported'} raise MacaroonError(X.get(err, 'operation failed unexpectedly')) cdef class Macaroon: cdef macaroon* _M def __cinit__(self): self._M = NULL def __dealloc__(self): if self._M != NULL: macaroon_destroy(self._M) self._M = NULL def validate(self): return macaroon_validate(self._M) == 0 @property def location(self): cdef const unsigned char* location = NULL cdef size_t location_sz = 0 self.assert_not_null() macaroon_location(self._M, &location, &location_sz) return location[:location_sz] @property def identifier(self): cdef const unsigned char* identifier = NULL cdef size_t identifier_sz = 0 self.assert_not_null() macaroon_identifier(self._M, &identifier, &identifier_sz) return identifier[:identifier_sz] @property def signature(self): cdef const unsigned char* signature = NULL cdef size_t signature_sz = 0 self.assert_not_null() macaroon_signature(self._M, &signature, &signature_sz) return (signature[:signature_sz]).encode('hex') def copy(self): self.assert_not_null() cdef macaroon_returncode err cdef Macaroon M = Macaroon() M._M = macaroon_copy(self._M, &err) if M._M == NULL: raise_error(err) return M def serialize(self): 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 = malloc(sizeof(unsigned char) * data_sz) if data == NULL: raise MemoryError if macaroon_serialize(self._M, data, data_sz, &err) < 0: raise_error(err) return bytes(data) finally: if data != NULL: free(data) def serialize_json(self): cdef char* data = NULL cdef size_t data_sz = 0 cdef macaroon_returncode err self.assert_not_null() try: data_sz = macaroon_serialize_json_size_hint(self._M) data = malloc(sizeof(unsigned char) * data_sz) if data == NULL: raise MemoryError if macaroon_serialize_json(self._M, data, data_sz, &err) < 0: raise_error(err) return bytes(data) finally: if data != NULL: free(data) def inspect(self): cdef char* data = NULL cdef size_t data_sz = 0 cdef macaroon_returncode err self.assert_not_null() try: data_sz = macaroon_inspect_size_hint(self._M) data = malloc(sizeof(unsigned char) * data_sz) if data == NULL: raise MemoryError if macaroon_inspect(self._M, data, data_sz, &err) < 0: raise_error(err) return bytes(data) finally: if data != NULL: free(data) def is_same(self, Macaroon M): self.assert_not_null() M.assert_not_null() return macaroon_cmp(self._M, M._M) == 0 def third_party_caveats(self): self.assert_not_null() cdef const unsigned char* location = NULL cdef size_t location_sz = 0 cdef const unsigned char* identifier = NULL cdef size_t identifier_sz = 0 cdef unsigned num = macaroon_num_third_party_caveats(self._M) ids = [] for i in range(num): if macaroon_third_party_caveat(self._M, i, &location, &location_sz, &identifier, &identifier_sz) < 0: raise_error(MACAROON_INVALID) ids.append((location[:location_sz], identifier[:identifier_sz])) return ids def prepare_for_request(self, Macaroon D): cdef macaroon_returncode err cdef Macaroon DP = Macaroon() self.assert_not_null() D.assert_not_null() DP._M = macaroon_prepare_for_request(self._M, D._M, &err) if DP._M == NULL: raise_error(err) return DP def add_first_party_caveat(self, bytes predicate): self.assert_not_null() cdef macarr cdef macaroon_returncode err cdef Macaroon M = Macaroon() M._M = macaroon_add_first_party_caveat(self._M, predicate, len(predicate), &err) if M._M == NULL: raise_error(err) return M def add_third_party_caveat(self, bytes _location, bytes _key, bytes _key_id): cdef unsigned char* location = _location cdef size_t location_sz = len(_location) cdef unsigned char* key = _key cdef size_t key_sz = len(_key) cdef unsigned char* key_id = _key_id cdef size_t key_id_sz = len(_key_id) cdef macaroon_returncode err cdef Macaroon M = Macaroon() self.assert_not_null() M._M = macaroon_add_third_party_caveat(self._M, location, location_sz, key, key_sz, key_id, key_id_sz, &err) if M._M == NULL: raise_error(err) return M cdef assert_not_null(self): if self._M == NULL: raise ValueError("macaroon not initialized") cdef int general_cb(void* f, const unsigned char* pred, size_t pred_sz): try: if (f)(pred[:pred_sz]): return 0 except: pass return -1 cdef class Verifier: cdef macaroon_verifier* _V cdef list _funcs def __cinit__(self): self._V = macaroon_verifier_create() if self._V == NULL: raise MemoryError self._funcs = [] def __dealloc__(self): if self._V != NULL: macaroon_verifier_destroy(self._V) self._V = NULL def satisfy_exact(self, pred): cdef macaroon_returncode err if macaroon_verifier_satisfy_exact(self._V, pred, len(pred), &err) < 0: raise_error(err) def satisfy_general(self, func): cdef macaroon_returncode err if macaroon_verifier_satisfy_general(self._V, general_cb, func, &err) < 0: raise_error(err) self._funcs.append(func) def verify(self, Macaroon M, bytes key, MS=None): if self.verify_unsafe(M, key, MS): return True else: raise Unauthorized("macaroon not authorized") def verify_unsafe(self, Macaroon M, bytes key, MS=None): cdef macaroon_returncode err cdef macaroon** discharges = NULL cdef Macaroon tmp try: M.assert_not_null() MS = MS or [] discharges = malloc(sizeof(macaroon*) * len(MS)) for i, D in enumerate(MS): tmp = D tmp.assert_not_null() discharges[i] = tmp._M rc = macaroon_verify(self._V, M._M, key, len(key), discharges, len(MS), &err) if rc == 0: return True elif err == MACAROON_NOT_AUTHORIZED: return False else: raise_error(err) finally: if discharges: free(discharges) def create(bytes _location, bytes _key, bytes _key_id): cdef unsigned char* location = _location cdef size_t location_sz = len(_location) cdef unsigned char* key = _key cdef size_t key_sz = len(_key) cdef unsigned char* key_id = _key_id cdef size_t key_id_sz = len(_key_id) cdef macaroon_returncode err cdef Macaroon M = Macaroon() M._M = macaroon_create(location, location_sz, key, key_sz, key_id, key_id_sz, &err) if M._M == NULL: raise_error(err) return M def deserialize(bytes m): cdef Macaroon M = Macaroon() cdef macaroon_returncode err M._M = macaroon_deserialize(m, &err) if M._M == NULL: raise_error(err) return M libmacaroons-releases-0.3.0/configure.ac000066400000000000000000000050111253111166600202510ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ([2.61]) AC_INIT([libmacaroons], [0.3.0], [robert@rescrv.net]) m4_define([serial_tests], [ m4_esyscmd([case `automake --version | head -n 1` in *1.11*);; *) echo serial-tests;; esac]) ]) AM_INIT_AUTOMAKE(foreign serial_tests subdir-objects dist-bzip2) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AM_PATH_PYTHON([2.6]) LT_PREREQ([2.2]) LT_INIT AC_CONFIG_SRCDIR([macaroons.h]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) # Checks for programs. AC_PROG_CC AC_PROG_CXX ANAL_WARNINGS # Checks for libraries. PKG_CHECK_MODULES([SODIUM], [libsodium >= 0.4]) # Checks for header files. # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_SIZE_T # Checks for library functions. # Optional components AC_ARG_ENABLE([python_bindings], [AS_HELP_STRING([--enable-python-bindings], [build Python bindings @<:@default: no@:>@])], [python_bindings=${enableval}], [python_bindings=no]) if test x"${python_bindings}" = xyes; then AC_PYTHON_DEVEL([>= '2.6']) fi AC_ARG_ENABLE([json_support], [AS_HELP_STRING([--enable-json-support], [enable support for JSON macaroons @<:@default: no@:>@])], [json_support=${enableval}], [json_support=no]) if test x"${json_support}" = xyes; then AC_DEFINE([MACAROONS_JSON_SUPPORT], [], [Support JSON macaroons]) AC_CHECK_LIB([json], [json_object_get_double], [needs_json=yes], [needs_json=no]) AC_CHECK_LIB([json-c], [json_object_get_double], [needs_json_c=yes], [needs_json_c=no]) if test x"${needs_json}" = xyes; then AC_SUBST([JSON_LIBS], ["-ljson"]) elif test x"${needs_json_c}" = xyes; then AC_SUBST([JSON_LIBS], ["-ljson-c"]) else AC_MSG_ERROR([ ---------------------------------------- Macaroons rely upon the libjson library. Please install libjson to continue. ----------------------------------------]) fi AC_CHECK_HEADER([json/json.h],,[AC_MSG_ERROR([ ---------------------------------------- Macaroons rely upon the libjson library. Please install libjson to continue. ----------------------------------------])]) fi AM_CONDITIONAL([ENABLE_PYTHON_BINDINGS], [test x"${python_bindings}" = xyes]) AM_CONDITIONAL([ENABLE_JSON_SUPPORT], [test x"${json_support}" = xyes]) AH_BOTTOM([#include ]) AC_CONFIG_FILES([Makefile libmacaroons.pc]) AC_OUTPUT libmacaroons-releases-0.3.0/constants.h000066400000000000000000000040431253111166600201540ustar00rootroot00000000000000/* Copyright (c) 2014, 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. */ #ifndef macaroons_constants_h_ #define macaroons_constants_h_ #define STRLENOF(x) (sizeof(x)-1) #define LOCATION "location" #define LOCATION_SZ STRLENOF(LOCATION) #define IDENTIFIER "identifier" #define IDENTIFIER_SZ STRLENOF(IDENTIFIER) #define SIGNATURE "signature" #define SIGNATURE_SZ STRLENOF(SIGNATURE) #define CID "cid" #define CID_SZ STRLENOF(CID) #define VID "vid" #define VID_SZ STRLENOF(VID) #define CL "cl" #define CL_SZ STRLENOF(CL) #endif /* macaroons_constants_h_ */ libmacaroons-releases-0.3.0/custom-config.h000066400000000000000000000032031253111166600207120ustar00rootroot00000000000000/* Copyright (c) 2014, 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. */ #ifdef NDEBUG #error macaroons should be built with assert enabled #endif libmacaroons-releases-0.3.0/libmacaroons.pc.in000066400000000000000000000003511253111166600213670ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libmacaroons Description: Macaroons are better than cookies Version: @VERSION@ Requires: Libs: -L${libdir} -lmacaroons Cflags: -I${includedir} libmacaroons-releases-0.3.0/m4/000077500000000000000000000000001253111166600163065ustar00rootroot00000000000000libmacaroons-releases-0.3.0/m4/.gitignore000066400000000000000000000000001253111166600202640ustar00rootroot00000000000000libmacaroons-releases-0.3.0/m4/anal_warnings.m4000066400000000000000000000230011253111166600213670ustar00rootroot00000000000000# Copyright (c) 2012-2013, 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. # # This macro enables many compiler warnings for C++ that generally catch bugs in # code. It offers the "--enable-wanal-flags" option which defaults to "no". AC_DEFUN([ANAL_WARNINGS], [WANAL_CFLAGS="" WANAL_CXXFLAGS="" WANAL_CFLAGS_ONLY="" AC_ARG_ENABLE([wanal-flags], [AS_HELP_STRING([--enable-wanal-flags], [enable many warnings @<:@default: no@:>@])], [wanal_flags=${enableval}], [wanal_flags=no]) if test x"${wanal_flags}" = xyes; then AX_CHECK_COMPILE_FLAG([-pedantic],[WANAL_CFLAGS="${WANAL_CFLAGS} -pedantic"],,) AX_CHECK_COMPILE_FLAG([-Wabi],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wabi"],,) AX_CHECK_COMPILE_FLAG([-Waddress],[WANAL_CFLAGS="${WANAL_CFLAGS} -Waddress"],,) AX_CHECK_COMPILE_FLAG([-Wall],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wall"],,) AX_CHECK_COMPILE_FLAG([-Warray-bounds],[WANAL_CFLAGS="${WANAL_CFLAGS} -Warray-bounds"],,) AX_CHECK_COMPILE_FLAG([-Wc++0x-compat],[WANAL_CXXFLAGS="${WANAL_CXXFLAGS} -Wc++0x-compat"],,) AX_CHECK_COMPILE_FLAG([-Wcast-align],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wcast-align"],,) AX_CHECK_COMPILE_FLAG([-Wcast-qual],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wcast-qual"],,) AX_CHECK_COMPILE_FLAG([-Wchar-subscripts],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wchar-subscripts"],,) AX_CHECK_COMPILE_FLAG([-Wclobbered],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wclobbered"],,) AX_CHECK_COMPILE_FLAG([-Wcomment],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wcomment"],,) #AX_CHECK_COMPILE_FLAG([-Wconversion],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wconversion"],,) AX_CHECK_COMPILE_FLAG([-Wctor-dtor-privacy],[WANAL_CXXFLAGS="${WANAL_CXXFLAGS} -Wctor-dtor-privacy"],,) AX_CHECK_COMPILE_FLAG([-Wdisabled-optimization],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wdisabled-optimization"],,) AX_CHECK_COMPILE_FLAG([-Weffc++],[WANAL_CXXFLAGS="${WANAL_CXXFLAGS} -Weffc++"],,) AX_CHECK_COMPILE_FLAG([-Wempty-body],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wempty-body"],,) AX_CHECK_COMPILE_FLAG([-Wenum-compare],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wenum-compare"],,) AX_CHECK_COMPILE_FLAG([-Wextra],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wextra"],,) AX_CHECK_COMPILE_FLAG([-Wfloat-equal],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wfloat-equal"],,) AX_CHECK_COMPILE_FLAG([-Wformat=2],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wformat=2"],,) AX_CHECK_COMPILE_FLAG([-Wformat-nonliteral],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wformat-nonliteral"],,) AX_CHECK_COMPILE_FLAG([-Wformat-security],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wformat-security"],,) AX_CHECK_COMPILE_FLAG([-Wformat],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wformat"],,) AX_CHECK_COMPILE_FLAG([-Wformat-y2k],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wformat-y2k"],,) AX_CHECK_COMPILE_FLAG([-Wignored-qualifiers],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wignored-qualifiers"],,) AX_CHECK_COMPILE_FLAG([-Wimplicit],[WANAL_CFLAGS_ONLY="${WANAL_CFLAGS} -Wimplicit"],,) AX_CHECK_COMPILE_FLAG([-Winit-self],[WANAL_CFLAGS="${WANAL_CFLAGS} -Winit-self"],,) AX_CHECK_COMPILE_FLAG([-Winline],[WANAL_CFLAGS="${WANAL_CFLAGS} -Winline"],,) AX_CHECK_COMPILE_FLAG([-Wlarger-than=4096],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wlarger-than=4096"],,) AX_CHECK_COMPILE_FLAG([-Wlogical-op],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wlogical-op"],,) AX_CHECK_COMPILE_FLAG([-Wmain],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wmain"],,) AX_CHECK_COMPILE_FLAG([-Wmissing-braces],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wmissing-braces"],,) AX_CHECK_COMPILE_FLAG([-Wmissing-field-initializers],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wmissing-field-initializers"],,) AX_CHECK_COMPILE_FLAG([-Wmissing-format-attribute],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wmissing-format-attribute"],,) AX_CHECK_COMPILE_FLAG([-Wmissing-include-dirs],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wmissing-include-dirs"],,) AX_CHECK_COMPILE_FLAG([-Wno-long-long],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wno-long-long"],,) AX_CHECK_COMPILE_FLAG([-Wnon-virtual-dtor],[WANAL_CXXFLAGS="${WANAL_CXXFLAGS} -Wnon-virtual-dtor"],,) AX_CHECK_COMPILE_FLAG([-Woverlength-strings],[WANAL_CFLAGS="${WANAL_CFLAGS} -Woverlength-strings"],,) AX_CHECK_COMPILE_FLAG([-Woverloaded-virtual],[WANAL_CXXFLAGS="${WANAL_CXXFLAGS} -Woverloaded-virtual"],,) AX_CHECK_COMPILE_FLAG([-Wpacked-bitfield-compat],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wpacked-bitfield-compat"],,) AX_CHECK_COMPILE_FLAG([-Wpacked],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wpacked"],,) #AX_CHECK_COMPILE_FLAG([-Wpadded],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wpadded"],,) AX_CHECK_COMPILE_FLAG([-Wparentheses],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wparentheses"],,) AX_CHECK_COMPILE_FLAG([-Wpointer-arith],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wpointer-arith"],,) AX_CHECK_COMPILE_FLAG([-Wredundant-decls],[WANAL_CXXFLAGS="${WANAL_CXXFLAGS} -Wredundant-decls"],,) AX_CHECK_COMPILE_FLAG([-Wreorder],[WANAL_CXXFLAGS="${WANAL_CXXFLAGS} -Wreorder"],,) AX_CHECK_COMPILE_FLAG([-Wreturn-type],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wreturn-type"],,) AX_CHECK_COMPILE_FLAG([-Wsequence-point],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wsequence-point"],,) AX_CHECK_COMPILE_FLAG([-Wshadow],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wshadow"],,) AX_CHECK_COMPILE_FLAG([-Wsign-compare],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wsign-compare"],,) #AX_CHECK_COMPILE_FLAG([-Wsign-conversion],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wsign-conversion"],,) AX_CHECK_COMPILE_FLAG([-Wsign-promo],[WANAL_CXXFLAGS="${WANAL_CXXFLAGS} -Wsign-promo"],,) AX_CHECK_COMPILE_FLAG([-Wstack-protector],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wstack-protector"],,) AX_CHECK_COMPILE_FLAG([-Wstrict-aliasing=3],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wstrict-aliasing=3"],,) AX_CHECK_COMPILE_FLAG([-Wstrict-aliasing],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wstrict-aliasing"],,) AX_CHECK_COMPILE_FLAG([-Wstrict-null-sentinel],[WANAL_CXXFLAGS="${WANAL_CXXFLAGS} -Wstrict-null-sentinel"],,) #AX_CHECK_COMPILE_FLAG([-Wstrict-overflow=4],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wstrict-overflow=4"],,) #AX_CHECK_COMPILE_FLAG([-Wstrict-overflow],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wstrict-overflow"],,) AX_CHECK_COMPILE_FLAG([-Wswitch-default],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wswitch-default"],,) AX_CHECK_COMPILE_FLAG([-Wswitch-enum],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wswitch-enum"],,) AX_CHECK_COMPILE_FLAG([-Wswitch],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wswitch"],,) AX_CHECK_COMPILE_FLAG([-Wtrigraphs],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wtrigraphs"],,) AX_CHECK_COMPILE_FLAG([-Wtype-limits],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wtype-limits"],,) AX_CHECK_COMPILE_FLAG([-Wundef],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wundef"],,) AX_CHECK_COMPILE_FLAG([-Wuninitialized],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wuninitialized"],,) AX_CHECK_COMPILE_FLAG([-Wunsafe-loop-optimizations],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wunsafe-loop-optimizations"],,) AX_CHECK_COMPILE_FLAG([-Wunused-function],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wunused-function"],,) AX_CHECK_COMPILE_FLAG([-Wunused-label],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wunused-label"],,) AX_CHECK_COMPILE_FLAG([-Wunused-parameter],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wunused-parameter"],,) AX_CHECK_COMPILE_FLAG([-Wunused-value],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wunused-value"],,) AX_CHECK_COMPILE_FLAG([-Wunused-variable],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wunused-variable"],,) AX_CHECK_COMPILE_FLAG([-Wunused],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wunused"],,) AX_CHECK_COMPILE_FLAG([-Wvolatile-register-var],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wvolatile-register-var"],,) AX_CHECK_COMPILE_FLAG([-Wwrite-strings],[WANAL_CFLAGS="${WANAL_CFLAGS} -Wwrite-strings"],,) AX_CHECK_COMPILE_FLAG([-Qunused-arguments],[WANAL_CFLAGS="${WANAL_CFLAGS} -Qunused-arguments"],,) fi WANAL_CXXFLAGS="${WANAL_CFLAGS} ${WANAL_CXXFLAGS}" WANAL_CFLAGS="${WANAL_CFLAGS} ${WANAL_CFLAGS_ONLY}" AC_SUBST([WANAL_CFLAGS], [${WANAL_CFLAGS}]) AC_SUBST([WANAL_CXXFLAGS], [${WANAL_CXXFLAGS}]) ]) libmacaroons-releases-0.3.0/m4/ax_check_compile_flag.m4000066400000000000000000000062511253111166600230220ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS]) # # DESCRIPTION # # Check whether the given FLAG works with the current language's compiler # or gives an error. (Warnings, however, are ignored) # # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on # success/failure. # # If EXTRA-FLAGS is defined, it is added to the current language's default # flags (e.g. CFLAGS) when the check is done. The check is thus made with # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to # force the compiler to issue an error when a bad flag is given. # # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 2 AC_DEFUN([AX_CHECK_COMPILE_FLAG], [AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes], [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ])dnl AX_CHECK_COMPILE_FLAGS libmacaroons-releases-0.3.0/m4/ax_check_link_flag.m4000066400000000000000000000057601253111166600223330ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS]) # # DESCRIPTION # # Check whether the given FLAG works with the linker or gives an error. # (Warnings, however, are ignored) # # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on # success/failure. # # If EXTRA-FLAGS is defined, it is added to the linker's default flags # when the check is done. The check is thus made with the flags: "LDFLAGS # EXTRA-FLAGS FLAG". This can for example be used to force the linker to # issue an error when a bad flag is given. # # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 2 AC_DEFUN([AX_CHECK_LINK_FLAG], [AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [ ax_check_save_flags=$LDFLAGS LDFLAGS="$LDFLAGS $4 $1" AC_LINK_IFELSE([AC_LANG_PROGRAM()], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) LDFLAGS=$ax_check_save_flags]) AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes], [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ])dnl AX_CHECK_LINK_FLAGS libmacaroons-releases-0.3.0/m4/ax_check_preproc_flag.m4000066400000000000000000000061541253111166600230460ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_check_preproc_flag.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_PREPROC_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS]) # # DESCRIPTION # # Check whether the given FLAG works with the current language's # preprocessor or gives an error. (Warnings, however, are ignored) # # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on # success/failure. # # If EXTRA-FLAGS is defined, it is added to the preprocessor's default # flags when the check is done. The check is thus made with the flags: # "CPPFLAGS EXTRA-FLAGS FLAG". This can for example be used to force the # preprocessor to issue an error when a bad flag is given. # # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{COMPILE,LINK}_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 2 AC_DEFUN([AX_CHECK_PREPROC_FLAG], [AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]cppflags_$4_$1])dnl AC_CACHE_CHECK([whether _AC_LANG preprocessor accepts $1], CACHEVAR, [ ax_check_save_flags=$CPPFLAGS CPPFLAGS="$CPPFLAGS $4 $1" AC_PREPROC_IFELSE([AC_LANG_PROGRAM()], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) CPPFLAGS=$ax_check_save_flags]) AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes], [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ])dnl AX_CHECK_PREPROC_FLAGS libmacaroons-releases-0.3.0/m4/ax_python_devel.m4000066400000000000000000000255771253111166600217600ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_python_devel.html # =========================================================================== # # SYNOPSIS # # AX_PYTHON_DEVEL([version]) # # DESCRIPTION # # Note: Defines as a precious variable "PYTHON_VERSION". Don't override it # in your configure.ac. # # This macro checks for Python and tries to get the include path to # 'Python.h'. It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LDFLAGS) # output variables. It also exports $(PYTHON_EXTRA_LIBS) and # $(PYTHON_EXTRA_LDFLAGS) for embedding Python in your code. # # You can search for some particular version of Python by passing a # parameter to this macro, for example ">= '2.3.1'", or "== '2.4'". Please # note that you *have* to pass also an operator along with the version to # match, and pay special attention to the single quotes surrounding the # version number. Don't use "PYTHON_VERSION" for this: that environment # variable is declared as precious and thus reserved for the end-user. # # This macro should work for all versions of Python >= 2.1.0. As an end # user, you can disable the check for the python version by setting the # PYTHON_NOVERSIONCHECK environment variable to something else than the # empty string. # # If you need to use this macro for an older Python version, please # contact the authors. We're always open for feedback. # # LICENSE # # Copyright (c) 2009 Sebastian Huber # Copyright (c) 2009 Alan W. Irwin # Copyright (c) 2009 Rafael Laboissiere # Copyright (c) 2009 Andrew Collier # Copyright (c) 2009 Matteo Settenvini # Copyright (c) 2009 Horst Knorr # Copyright (c) 2013 Daniel Mullner # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 16 AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL]) AC_DEFUN([AX_PYTHON_DEVEL],[ # # Allow the use of a (user set) custom python version # AC_ARG_VAR([PYTHON_VERSION],[The installed Python version to use, for example '2.3'. This string will be appended to the Python interpreter canonical name.]) AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]]) if test -z "$PYTHON"; then AC_MSG_ERROR([Cannot find python$PYTHON_VERSION in your system path]) PYTHON_VERSION="" fi # # Check for a version of Python >= 2.1.0 # AC_MSG_CHECKING([for a version of Python >= '2.1.0']) ac_supports_python_ver=`$PYTHON -c "import sys; \ ver = sys.version.split ()[[0]]; \ print (ver >= '2.1.0')"` if test "$ac_supports_python_ver" != "True"; then if test -z "$PYTHON_NOVERSIONCHECK"; then AC_MSG_RESULT([no]) AC_MSG_FAILURE([ This version of the AC@&t@_PYTHON_DEVEL macro doesn't work properly with versions of Python before 2.1.0. You may need to re-run configure, setting the variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG, PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand. Moreover, to disable this check, set PYTHON_NOVERSIONCHECK to something else than an empty string. ]) else AC_MSG_RESULT([skip at user request]) fi else AC_MSG_RESULT([yes]) fi # # if the macro parameter ``version'' is set, honour it # if test -n "$1"; then AC_MSG_CHECKING([for a version of Python $1]) ac_supports_python_ver=`$PYTHON -c "import sys; \ ver = sys.version.split ()[[0]]; \ print (ver $1)"` if test "$ac_supports_python_ver" = "True"; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) AC_MSG_ERROR([this package requires Python $1. If you have it installed, but it isn't the default Python interpreter in your system path, please pass the PYTHON_VERSION variable to configure. See ``configure --help'' for reference. ]) PYTHON_VERSION="" fi fi # # Check if you have distutils, else fail # AC_MSG_CHECKING([for the distutils Python package]) ac_distutils_result=`$PYTHON -c "import distutils" 2>&1` if test -z "$ac_distutils_result"; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) AC_MSG_ERROR([cannot import Python module "distutils". Please check your Python installation. The error was: $ac_distutils_result]) PYTHON_VERSION="" fi # # Check for Python include path # AC_MSG_CHECKING([for Python include path]) if test -z "$PYTHON_CPPFLAGS"; then python_path=`$PYTHON -c "import distutils.sysconfig; \ print (distutils.sysconfig.get_python_inc ());"` plat_python_path=`$PYTHON -c "import distutils.sysconfig; \ print (distutils.sysconfig.get_python_inc (plat_specific=1));"` if test -n "${python_path}"; then if test "${plat_python_path}" != "${python_path}"; then python_path="-I$python_path -I$plat_python_path" else python_path="-I$python_path" fi fi PYTHON_CPPFLAGS=$python_path fi AC_MSG_RESULT([$PYTHON_CPPFLAGS]) AC_SUBST([PYTHON_CPPFLAGS]) # # Check for Python library path # AC_MSG_CHECKING([for Python library path]) if test -z "$PYTHON_LDFLAGS"; then # (makes two attempts to ensure we've got a version number # from the interpreter) ac_python_version=`cat<]], [[Py_Initialize();]]) ],[pythonexists=yes],[pythonexists=no]) AC_LANG_POP([C]) # turn back to default flags CPPFLAGS="$ac_save_CPPFLAGS" LIBS="$ac_save_LIBS" AC_MSG_RESULT([$pythonexists]) if test ! "x$pythonexists" = "xyes"; then AC_MSG_FAILURE([ Could not link test program to Python. Maybe the main Python library has been installed in some non-standard library path. If so, pass it to configure, via the LDFLAGS environment variable. Example: ./configure LDFLAGS="-L/usr/non-standard-path/python/lib" ============================================================================ ERROR! You probably have to install the development version of the Python package for your distribution. The exact name of this package varies among them. ============================================================================ ]) PYTHON_VERSION="" fi # # all done! # ]) libmacaroons-releases-0.3.0/macaroons.c000066400000000000000000001453671253111166600201340ustar00rootroot00000000000000/* Copyright (c) 2014, 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* C */ #include #include #include /* json */ #ifdef MACAROONS_JSON_SUPPORT #include #endif /* macaroons */ #include "base64.h" #include "constants.h" #include "macaroons.h" #include "packet.h" #include "port.h" #ifdef PARANOID_MACAROONS #define VALIDATE(M) assert(macaroon_validate(M) == 0); #else #define VALIDATE(M) do {} while (0) #endif #define MACAROON_API __attribute__ ((visibility ("default"))) #if MACAROON_HASH_BYTES != MACAROON_SECRET_KEY_BYTES #error bad constants #endif #if MACAROON_HASH_BYTES != MACAROON_SUGGESTED_SECRET_LENGTH #error bad constants #endif struct caveat { struct packet cid; struct packet vid; struct packet cl; }; struct predicate { const unsigned char* data; size_t size; unsigned char* alloc; }; struct verifier_callback { int (*func)(void* f, const unsigned char* pred, size_t pred_sz); void* ptr; }; struct macaroon { /* the location packet */ struct packet location; /* the identifier packet */ struct packet identifier; /* the signature packet */ struct packet signature; /* zero or more caveats */ size_t num_caveats; struct caveat caveats[1]; }; struct macaroon_verifier { struct predicate* predicates; size_t predicates_sz; size_t predicates_cap; struct verifier_callback* verifier_callbacks; size_t verifier_callbacks_sz; size_t verifier_callbacks_cap; }; /* Allocate a new macaroon with space for "num_caveats" caveats and a body of * "body_data" bytes. Returns a ptr to a contiguous set of "body_data" bytes to * which the callee may write. */ static struct macaroon* macaroon_malloc(const size_t num_caveats, const size_t body_data, unsigned char** _ptr) { unsigned char* ptr = NULL; struct macaroon* M = NULL; const size_t additional_caveats = (num_caveats > 0) ? num_caveats - 1 : 0; const size_t sz = sizeof(struct macaroon) + body_data + additional_caveats * sizeof(struct caveat); M = malloc(sz); if (!M) { return NULL; } macaroon_memzero(M, sz); ptr = (unsigned char*) M; ptr += sizeof(struct macaroon); ptr += additional_caveats * sizeof(struct caveat); *_ptr = ptr; return M; } /* cumulative packet size, excluding the signature packet */ static size_t macaroon_body_size(const struct macaroon* M) { size_t i = 0; size_t sz = M->location.size + M->identifier.size; for (i = 0; i < M->num_caveats; ++i) { sz += M->caveats[i].cid.size; sz += M->caveats[i].vid.size; sz += M->caveats[i].cl.size; } return sz; } MACAROON_API struct macaroon* macaroon_create_raw(const unsigned char* location, size_t location_sz, const unsigned char* key, size_t key_sz, const unsigned char* id, size_t id_sz, enum macaroon_returncode* err) { unsigned char hash[MACAROON_HASH_BYTES]; size_t sz; struct macaroon* M; unsigned char* ptr; assert(location_sz < MACAROON_MAX_STRLEN); assert(id_sz < MACAROON_MAX_STRLEN); assert(key_sz == MACAROON_SUGGESTED_SECRET_LENGTH); if (macaroon_hmac(key, key_sz, id, id_sz, hash) < 0) { *err = MACAROON_HASH_FAILED; return NULL; } sz = PACKET_SIZE(LOCATION, location_sz) + PACKET_SIZE(IDENTIFIER, id_sz) + PACKET_SIZE(SIGNATURE, MACAROON_HASH_BYTES); M = macaroon_malloc(0, sz, &ptr); if (!M) { *err = MACAROON_OUT_OF_MEMORY; return NULL; } ptr = create_location_packet(location, location_sz, &M->location, ptr); ptr = create_identifier_packet(id, id_sz, &M->identifier, ptr); ptr = create_signature_packet(hash, MACAROON_HASH_BYTES, &M->signature, ptr); VALIDATE(M); return M; } static int generate_derived_key(const unsigned char* variable_key, size_t variable_key_sz, unsigned char* derived_key) { unsigned char genkey[MACAROON_HASH_BYTES]; macaroon_memzero(genkey, MACAROON_HASH_BYTES); memmove(genkey, "macaroons-key-generator", sizeof("macaroons-key-generator")); return macaroon_hmac(genkey, MACAROON_HASH_BYTES, variable_key, variable_key_sz, derived_key); } MACAROON_API struct macaroon* macaroon_create(const unsigned char* location, size_t location_sz, const unsigned char* key, size_t key_sz, const unsigned char* id, size_t id_sz, enum macaroon_returncode* err) { unsigned char derived_key[MACAROON_HASH_BYTES]; if (generate_derived_key(key, key_sz, derived_key) < 0) { *err = MACAROON_HASH_FAILED; return NULL; } return macaroon_create_raw(location, location_sz, derived_key, MACAROON_HASH_BYTES, id, id_sz, err); } MACAROON_API void macaroon_destroy(struct macaroon* M) { if (M) { free(M); } } MACAROON_API int macaroon_validate(const struct macaroon* M) { /* XXX */ (void) M; return 0; } static int macaroon_hash1(const unsigned char* key, const unsigned char* data1, size_t data1_sz, unsigned char* hash) { return macaroon_hmac(key, MACAROON_HASH_BYTES, data1, data1_sz, hash); } MACAROON_API struct macaroon* macaroon_add_first_party_caveat(const struct macaroon* N, const unsigned char* predicate, size_t predicate_sz, enum macaroon_returncode* err) { const unsigned char* key; unsigned char hash[MACAROON_HASH_BYTES]; size_t i; size_t sz; struct macaroon* M; unsigned char* ptr; assert(predicate_sz < MACAROON_MAX_STRLEN); assert(N->signature.data && N->signature.size > PACKET_PREFIX); if (N->num_caveats + 1 > MACAROON_MAX_CAVEATS) { *err = MACAROON_TOO_MANY_CAVEATS; return NULL; } if (parse_signature_packet(&N->signature, &key) < 0) { *err = MACAROON_INVALID; return NULL; } if (macaroon_hash1(key, predicate, predicate_sz, hash) < 0) { *err = MACAROON_HASH_FAILED; return NULL; } sz = macaroon_body_size(N); sz += PACKET_SIZE(CID_SZ, predicate_sz); sz += PACKET_SIZE(SIGNATURE, MACAROON_HASH_BYTES); M = macaroon_malloc(N->num_caveats + 1, sz, &ptr); if (!M) { *err = MACAROON_OUT_OF_MEMORY; return NULL; } M->num_caveats = N->num_caveats + 1; ptr = copy_packet(&N->location, &M->location, ptr); ptr = copy_packet(&N->identifier, &M->identifier, ptr); for (i = 0; i < N->num_caveats; ++i) { ptr = copy_packet(&N->caveats[i].cid, &M->caveats[i].cid, ptr); ptr = copy_packet(&N->caveats[i].vid, &M->caveats[i].vid, ptr); ptr = copy_packet(&N->caveats[i].cl, &M->caveats[i].cl, ptr); } ptr = create_cid_packet(predicate, predicate_sz, &M->caveats[M->num_caveats - 1].cid, ptr); ptr = create_signature_packet(hash, MACAROON_HASH_BYTES, &M->signature, ptr); VALIDATE(M); return M; } static int macaroon_hash2(const unsigned char* key, const unsigned char* data1, size_t data1_sz, const unsigned char* data2, size_t data2_sz, unsigned char* hash) { int rc = 0; unsigned char tmp[2 * MACAROON_HASH_BYTES]; rc |= macaroon_hmac(key, MACAROON_HASH_BYTES, data1, data1_sz, tmp); rc |= macaroon_hmac(key, MACAROON_HASH_BYTES, data2, data2_sz, tmp + MACAROON_HASH_BYTES); rc |= macaroon_hmac(key, MACAROON_HASH_BYTES, tmp, 2 * MACAROON_HASH_BYTES, hash); return rc; } #define SECRET_BOX_OVERHEAD \ (MACAROON_SECRET_TEXT_ZERO_BYTES \ - MACAROON_SECRET_BOX_ZERO_BYTES) #define VID_NONCE_KEY_SZ \ (MACAROON_SECRET_NONCE_BYTES \ + MACAROON_HASH_BYTES \ + SECRET_BOX_OVERHEAD) #if MACAROON_SECRET_TEXT_ZERO_BYTES < MACAROON_SECRET_BOX_ZERO_BYTES #error bad constants #endif MACAROON_API struct macaroon* macaroon_add_third_party_caveat_raw(const struct macaroon* N, const unsigned char* location, size_t location_sz, const unsigned char* key, size_t key_sz, const unsigned char* id, size_t id_sz, enum macaroon_returncode* err) { unsigned char new_sig[MACAROON_HASH_BYTES]; const unsigned char *old_sig; unsigned char enc_nonce[MACAROON_SECRET_NONCE_BYTES]; unsigned char enc_plaintext[MACAROON_SECRET_TEXT_ZERO_BYTES + MACAROON_HASH_BYTES]; unsigned char enc_ciphertext[MACAROON_SECRET_BOX_ZERO_BYTES + MACAROON_HASH_BYTES]; unsigned char vid[VID_NONCE_KEY_SZ]; size_t i; size_t sz; struct macaroon* M; unsigned char* ptr; assert(location_sz < MACAROON_MAX_STRLEN); assert(id_sz < MACAROON_MAX_STRLEN); assert(key_sz == MACAROON_SUGGESTED_SECRET_LENGTH); assert(N->signature.data && N->signature.size > PACKET_PREFIX); VALIDATE(N); if (N->num_caveats + 1 > MACAROON_MAX_CAVEATS) { *err = MACAROON_TOO_MANY_CAVEATS; return NULL; } /* * note that MACAROON_HASH_BYTES is the same as * MACAROON_SECRET_KEY_BYTES, so the signature is also good to use * as an encoding key */ if (parse_signature_packet(&N->signature, &old_sig) < 0) { *err = MACAROON_INVALID; return NULL; } macaroon_randombytes(enc_nonce, sizeof(enc_nonce)); macaroon_memzero(enc_plaintext, sizeof(enc_plaintext)); macaroon_memzero(enc_ciphertext, sizeof(enc_ciphertext)); /* now encrypt the key to give us vid */ memmove(enc_plaintext + MACAROON_SECRET_TEXT_ZERO_BYTES, key, key_sz < MACAROON_HASH_BYTES ? key_sz : MACAROON_HASH_BYTES); if (macaroon_secretbox(old_sig, enc_nonce, enc_plaintext, MACAROON_SECRET_TEXT_ZERO_BYTES + MACAROON_HASH_BYTES, enc_ciphertext) < 0) { *err = MACAROON_HASH_FAILED; return NULL; } /* copy the (nonce, vid) pair into vid */ memmove(vid, enc_nonce, MACAROON_SECRET_NONCE_BYTES); memmove(vid + MACAROON_SECRET_NONCE_BYTES, enc_ciphertext + MACAROON_SECRET_BOX_ZERO_BYTES, VID_NONCE_KEY_SZ - MACAROON_SECRET_NONCE_BYTES); /* calculate the new signature */ if (macaroon_hash2(old_sig, vid, VID_NONCE_KEY_SZ, id, id_sz, new_sig) < 0) { *err = MACAROON_HASH_FAILED; return NULL; } sz = macaroon_body_size(N); sz += PACKET_SIZE(CID_SZ, id_sz); sz += PACKET_SIZE(VID_SZ, VID_NONCE_KEY_SZ); sz += PACKET_SIZE(CL_SZ, location_sz); sz += PACKET_SIZE(SIGNATURE, MACAROON_HASH_BYTES); M = macaroon_malloc(N->num_caveats + 1, sz, &ptr); if (!M) { *err = MACAROON_OUT_OF_MEMORY; return NULL; } M->num_caveats = N->num_caveats + 1; ptr = copy_packet(&N->location, &M->location, ptr); ptr = copy_packet(&N->identifier, &M->identifier, ptr); for (i = 0; i < N->num_caveats; ++i) { ptr = copy_packet(&N->caveats[i].cid, &M->caveats[i].cid, ptr); ptr = copy_packet(&N->caveats[i].vid, &M->caveats[i].vid, ptr); ptr = copy_packet(&N->caveats[i].cl, &M->caveats[i].cl, ptr); } ptr = create_cid_packet(id, id_sz, &M->caveats[M->num_caveats - 1].cid, ptr); ptr = create_vid_packet(vid, VID_NONCE_KEY_SZ, &M->caveats[M->num_caveats - 1].vid, ptr); ptr = create_cl_packet(location, location_sz, &M->caveats[M->num_caveats - 1].cl, ptr); ptr = create_signature_packet(new_sig, MACAROON_HASH_BYTES, &M->signature, ptr); VALIDATE(M); return M; } MACAROON_API struct macaroon* macaroon_add_third_party_caveat(const struct macaroon* N, const unsigned char* location, size_t location_sz, const unsigned char* key, size_t key_sz, const unsigned char* id, size_t id_sz, enum macaroon_returncode* err) { unsigned char derived_key[MACAROON_HASH_BYTES]; if (generate_derived_key(key, key_sz, derived_key) < 0) { *err = MACAROON_HASH_FAILED; return NULL; } return macaroon_add_third_party_caveat_raw(N, location, location_sz, derived_key, MACAROON_HASH_BYTES, id, id_sz, err); } static int macaroon_bind(const unsigned char* Msig, const unsigned char* MPsig, unsigned char* bound) { unsigned char key[MACAROON_HASH_BYTES]; macaroon_memzero(key, MACAROON_HASH_BYTES); return macaroon_hash2(key, Msig, MACAROON_HASH_BYTES, MPsig, MACAROON_HASH_BYTES, bound); } MACAROON_API unsigned macaroon_num_third_party_caveats(const struct macaroon* M) { size_t idx = 0; unsigned count = 0; VALIDATE(M); for (idx = 0; idx < M->num_caveats; ++idx) { if (M->caveats[idx].vid.size > 0 && M->caveats[idx].cl.size > 0) { ++count; } } return count; } MACAROON_API int macaroon_third_party_caveat(const struct macaroon* M, unsigned which, const unsigned char** location, size_t* location_sz, const unsigned char** identifier, size_t* identifier_sz) { int rc = 0; size_t idx = 0; unsigned count = 0; VALIDATE(M); for (idx = 0; idx < M->num_caveats; ++idx) { if (M->caveats[idx].vid.size > 0 && M->caveats[idx].cl.size > 0) { if (count == which) { rc = parse_cid_packet(&M->caveats[idx].cid, identifier, identifier_sz); assert(rc == 0); rc = parse_cl_packet(&M->caveats[idx].cl, location, location_sz); assert(rc == 0); return 0; } ++count; } } return -1; } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-qual" MACAROON_API struct macaroon* macaroon_prepare_for_request(const struct macaroon* M, const struct macaroon* D, enum macaroon_returncode* err) { const unsigned char* Msig; const unsigned char* MPsig; struct macaroon* B; unsigned char* ptr; unsigned char hash[MACAROON_HASH_BYTES]; VALIDATE(M); VALIDATE(D); if (parse_signature_packet(&M->signature, &Msig) < 0 || parse_signature_packet(&D->signature, &MPsig) < 0) { *err = MACAROON_INVALID; return NULL; } if (macaroon_bind(Msig, MPsig, hash) < 0) { *err = MACAROON_HASH_FAILED; return NULL; } B = macaroon_copy(D, err); if (!B) { return NULL; } ptr = (unsigned char*) B->signature.data; ptr = create_signature_packet(hash, MACAROON_HASH_BYTES, &B->signature, ptr); VALIDATE(B); return B; } #pragma GCC diagnostic pop MACAROON_API struct macaroon_verifier* macaroon_verifier_create() { struct macaroon_verifier* V; V = malloc(sizeof(struct macaroon_verifier)); if (!V) { return NULL; } memset(V, 0, sizeof(struct macaroon_verifier)); V->predicates = NULL; V->predicates_sz = 0; V->predicates_cap = 0; return V; } MACAROON_API void macaroon_verifier_destroy(struct macaroon_verifier* V) { size_t idx = 0; if (V) { for (idx = 0; idx < V->predicates_sz; ++idx) { if (V->predicates[idx].alloc) { free(V->predicates[idx].alloc); } } if (V->predicates) { free(V->predicates); } if (V->verifier_callbacks) { free(V->verifier_callbacks); } free(V); } } MACAROON_API int macaroon_verifier_satisfy_exact(struct macaroon_verifier* V, const unsigned char* predicate, size_t predicate_sz, enum macaroon_returncode* err) { struct predicate* tmp = NULL; if (V->predicates_sz == V->predicates_cap) { V->predicates_cap = V->predicates_cap < 8 ? 8 : V->predicates_cap + (V->predicates_cap >> 1); tmp = realloc(V->predicates, V->predicates_cap * sizeof(struct predicate)); if (!tmp) { *err = MACAROON_OUT_OF_MEMORY; return -1; } V->predicates = tmp; } assert(V->predicates_sz < V->predicates_cap); tmp = &V->predicates[V->predicates_sz]; tmp->data = tmp->alloc = malloc(sizeof(unsigned char) * predicate_sz); tmp->size = predicate_sz; if (!tmp->data) { *err = MACAROON_OUT_OF_MEMORY; return -1; } memmove(tmp->alloc, predicate, predicate_sz); ++V->predicates_sz; assert(V->predicates_sz <= V->predicates_cap); return 0; } MACAROON_API int macaroon_verifier_satisfy_general(struct macaroon_verifier* V, int (*general_check)(void* f, const unsigned char* pred, size_t pred_sz), void* f, enum macaroon_returncode* err) { struct verifier_callback* tmp = NULL; if (V->verifier_callbacks_sz == V->verifier_callbacks_cap) { V->verifier_callbacks_cap = V->verifier_callbacks_cap < 8 ? 8 : V->verifier_callbacks_cap + (V->verifier_callbacks_cap >> 1); tmp = realloc(V->verifier_callbacks, V->verifier_callbacks_cap * sizeof(struct verifier_callback)); if (!tmp) { *err = MACAROON_OUT_OF_MEMORY; return -1; } V->verifier_callbacks = tmp; } assert(V->verifier_callbacks_sz < V->verifier_callbacks_cap); tmp = &V->verifier_callbacks[V->verifier_callbacks_sz]; tmp->func = general_check; tmp->ptr = f; ++V->verifier_callbacks_sz; assert(V->verifier_callbacks_sz <= V->verifier_callbacks_cap); return 0; } static int macaroon_verify_inner(const struct macaroon_verifier* V, const struct macaroon* M, const struct macaroon* TM, const unsigned char* key, size_t key_sz, struct macaroon** MS, size_t MS_sz, enum macaroon_returncode* err, size_t* tree, size_t tree_idx); static int macaroon_verify_inner_1st(const struct macaroon_verifier* V, const struct caveat* C) { int fail = 0; int found = 0; size_t sz = 0; size_t idx = 0; struct predicate pred; struct predicate* poss; struct verifier_callback* vcb; pred.data = NULL; pred.size = 0; fail |= parse_cid_packet(&C->cid, &pred.data, &pred.size); for (idx = 0; idx < V->predicates_sz; ++idx) { poss = &V->predicates[idx]; sz = pred.size < poss->size ? pred.size : poss->size; found |= macaroon_memcmp(pred.data, poss->data, sz) == 0 && pred.size == poss->size; } for (idx = 0; idx < V->verifier_callbacks_sz; ++idx) { vcb = &V->verifier_callbacks[idx]; found |= vcb->func(vcb->ptr, pred.data, pred.size) == 0; } return (!fail && found) ? 0 : -1; } static int macaroon_verify_inner_3rd(const struct macaroon_verifier* V, const struct caveat* C, const unsigned char* sig, const struct macaroon* TM, struct macaroon** MS, size_t MS_sz, enum macaroon_returncode* err, size_t* tree, size_t tree_idx) { unsigned char enc_key[MACAROON_SECRET_KEY_BYTES]; const unsigned char *enc_nonce; unsigned char enc_plaintext[MACAROON_SECRET_TEXT_ZERO_BYTES + MACAROON_HASH_BYTES]; unsigned char enc_ciphertext[MACAROON_SECRET_BOX_ZERO_BYTES + MACAROON_HASH_BYTES + SECRET_BOX_OVERHEAD]; unsigned char vid_data[VID_NONCE_KEY_SZ]; int fail = 0; size_t midx = 0; size_t tidx = 0; struct predicate cav; struct predicate vid; struct predicate mac; size_t sz; cav.data = NULL; cav.size = 0; fail |= parse_cid_packet(&C->cid, &cav.data, &cav.size); tree[tree_idx] = MS_sz; for (midx = 0; midx < MS_sz; ++midx) { mac.data = NULL; mac.size = 0; fail |= parse_identifier_packet(&MS[midx]->identifier, &mac.data, &mac.size); sz = cav.size < mac.size ? cav.size : mac.size; if (macaroon_memcmp(cav.data, mac.data, sz) == 0 && cav.size == mac.size) { tree[tree_idx] = midx; } for (tidx = 0; tidx < tree_idx; ++tidx) { fail |= tree[tidx] == tree[tree_idx]; } } if (tree[tree_idx] < MS_sz) { /* zero everything */ macaroon_memzero(enc_key, sizeof(enc_key)); macaroon_memzero(enc_plaintext, sizeof(enc_plaintext)); macaroon_memzero(enc_ciphertext, sizeof(enc_ciphertext)); vid.data = vid_data; vid.size = sizeof(vid_data); fail |= parse_vid_packet(&C->vid, &vid.data, &vid.size); assert(vid.size == VID_NONCE_KEY_SZ); /* * the nonce is in the first MACAROON_SECRET_NONCE_BYTES * of the vid; the ciphertext is in the rest of it. */ enc_nonce = vid.data; /* fill in the ciphertext */ memmove(enc_ciphertext + MACAROON_SECRET_BOX_ZERO_BYTES, vid.data + MACAROON_SECRET_NONCE_BYTES, vid.size - MACAROON_SECRET_NONCE_BYTES); /* now get the plaintext */ fail |= macaroon_secretbox_open(sig, enc_nonce, enc_ciphertext, sizeof(enc_ciphertext), enc_plaintext); fail |= macaroon_verify_inner(V, MS[tree[tree_idx]], TM, enc_plaintext + MACAROON_SECRET_TEXT_ZERO_BYTES, MACAROON_HASH_BYTES, MS, MS_sz, err, tree, tree_idx + 1); } else { fail = -1; } return fail; } int macaroon_verify_inner(const struct macaroon_verifier* V, const struct macaroon* M, const struct macaroon* TM, const unsigned char* key, size_t key_sz, struct macaroon** MS, size_t MS_sz, enum macaroon_returncode* err, size_t* tree, size_t tree_idx) { size_t cidx = 0; int tree_fail = 0; const unsigned char* data = NULL; size_t data_sz = 0; const unsigned char* vdata = NULL; size_t vdata_sz = 0; unsigned char tmp[MACAROON_HASH_BYTES]; unsigned char csig[MACAROON_HASH_BYTES]; assert(M); assert(TM); if (macaroon_validate(M) < 0) { *err = MACAROON_INVALID; return -1; } if (tree_idx > MS_sz) { *err = MACAROON_CYCLE; return -1; } tree_fail = 0; data = NULL; data_sz = 0; tree_fail |= parse_identifier_packet(&M->identifier, &data, &data_sz); tree_fail |= macaroon_hmac(key, key_sz, data, data_sz, csig); for (cidx = 0; cidx < M->num_caveats; ++cidx) { if (M->caveats[cidx].vid.size == 0) { tree_fail |= macaroon_verify_inner_1st(V, M->caveats + cidx); /* move the signature and compute a new one */ memmove(tmp, csig, MACAROON_HASH_BYTES); data = NULL; data_sz = 0; tree_fail |= parse_cid_packet(&M->caveats[cidx].cid, &data, &data_sz); tree_fail |= macaroon_hash1(tmp, data, data_sz, csig); } else { tree_fail |= macaroon_verify_inner_3rd(V, M->caveats + cidx, csig, TM, MS, MS_sz, err, tree, tree_idx); /* move the signature and compute a new one */ memmove(tmp, csig, MACAROON_HASH_BYTES); data = NULL; data_sz = 0; tree_fail |= parse_cid_packet(&M->caveats[cidx].cid, &data, &data_sz); vdata = NULL; vdata_sz = 0; tree_fail |= parse_vid_packet(&M->caveats[cidx].vid, &vdata, &vdata_sz); tree_fail |= macaroon_hash2(tmp, vdata, vdata_sz, data, data_sz, csig); } } if (tree_idx > 0) { memmove(tmp, csig, MACAROON_HASH_BYTES); data = TM->signature.data; tree_fail |= parse_signature_packet(&TM->signature, &data); tree_fail |= macaroon_bind(data, tmp, csig); } data = M->signature.data; tree_fail |= parse_signature_packet(&M->signature, &data); tree_fail |= macaroon_memcmp(data, csig, MACAROON_HASH_BYTES); return tree_fail; } MACAROON_API int macaroon_verify_raw(const struct macaroon_verifier* V, const struct macaroon* M, const unsigned char* key, size_t key_sz, struct macaroon** MS, size_t MS_sz, enum macaroon_returncode* err) { int rc = 0; size_t i = 0; size_t* tree = malloc((MS_sz + 1) * sizeof(size_t)); if (!tree) { *err = MACAROON_OUT_OF_MEMORY; return -1; } for (i = 0; i < MS_sz; ++i) { tree[i] = MS_sz; } tree[MS_sz] = MS_sz; assert(key_sz == MACAROON_SUGGESTED_SECRET_LENGTH); rc = macaroon_verify_inner(V, M, M, key, key_sz, MS, MS_sz, err, tree, 0); if (rc) { *err = MACAROON_NOT_AUTHORIZED; } free(tree); return rc; } MACAROON_API int macaroon_verify(const struct macaroon_verifier* V, const struct macaroon* M, const unsigned char* key, size_t key_sz, struct macaroon** MS, size_t MS_sz, enum macaroon_returncode* err) { unsigned char derived_key[MACAROON_HASH_BYTES]; if (generate_derived_key(key, key_sz, derived_key) < 0) { *err = MACAROON_HASH_FAILED; return -1; } return macaroon_verify_raw(V, M, derived_key, MACAROON_HASH_BYTES, MS, MS_sz, err); } MACAROON_API void macaroon_location(const struct macaroon* M, const unsigned char** location, size_t* location_sz) { int rc = 0; assert(M); VALIDATE(M); rc = parse_location_packet(&M->location, location, location_sz); assert(rc == 0); } MACAROON_API void macaroon_identifier(const struct macaroon* M, const unsigned char** identifier, size_t* identifier_sz) { int rc = 0; assert(M); VALIDATE(M); rc = parse_identifier_packet(&M->identifier, identifier, identifier_sz); assert(rc == 0); } MACAROON_API void macaroon_signature(const struct macaroon* M, const unsigned char** signature, size_t* signature_sz) { int rc = 0; assert(M); VALIDATE(M); rc = parse_signature_packet(&M->signature, signature); *signature_sz = MACAROON_HASH_BYTES; assert(rc == 0); } enum encoding { ENCODING_RAW, ENCODING_BASE64, ENCODING_HEX }; static size_t encoded_size(enum encoding encoding, size_t data_sz) { switch (encoding) { case ENCODING_HEX: return data_sz * 2; case ENCODING_BASE64: return (data_sz + 2) / 3 * 4; case ENCODING_RAW: return data_sz; default: assert(0); } } /* * encode encodes the given data, putting * the resulting data and size into result and result_sz. * On return, if *result != data, the caller is * responsible for freeing it. */ static int encode(enum encoding encoding, const unsigned char* val, size_t val_sz, const unsigned char** result, size_t* result_sz, enum macaroon_returncode* err) { char* enc; int enc_sz; if (encoding == ENCODING_RAW) { *result = val; *result_sz = val_sz; return 0; } enc_sz = encoded_size(encoding, val_sz); enc = malloc(enc_sz + 1); if (enc == NULL) { *err = MACAROON_OUT_OF_MEMORY; return -1; } switch (encoding) { case ENCODING_BASE64: enc_sz = b64_ntop(val, val_sz, enc, enc_sz + 1); if (enc_sz < 0) { *err = MACAROON_BUF_TOO_SMALL; return -1; } break; case ENCODING_HEX: macaroon_bin2hex(val, val_sz, enc); break; case ENCODING_RAW: /* should never get here */ default: assert(0); } *result = (const unsigned char*)enc; *result_sz = enc_sz; return 0; } static size_t macaroon_inner_size_hint(const struct macaroon* M) { size_t i; size_t sz = M->location.size + M->identifier.size + M->signature.size; assert(M); VALIDATE(M); for (i = 0; i < M->num_caveats; ++i) { sz += M->caveats[i].cid.size; sz += M->caveats[i].vid.size; sz += M->caveats[i].cl.size; } return sz; } static size_t macaroon_inner_size_hint_ascii(const struct macaroon* M) { size_t i; size_t sz = M->location.size + M->identifier.size + encoded_size(ENCODING_HEX, M->signature.size); assert(M); VALIDATE(M); for (i = 0; i < M->num_caveats; ++i) { sz += M->caveats[i].cid.size; sz += encoded_size(ENCODING_BASE64, M->caveats[i].vid.size); sz += M->caveats[i].cl.size; } return sz; } MACAROON_API size_t macaroon_serialize_size_hint(const struct macaroon* M) { return encoded_size(ENCODING_BASE64, macaroon_inner_size_hint(M)) + 1; } MACAROON_API int macaroon_serialize(const struct macaroon* M, char* data, size_t data_sz, enum macaroon_returncode* err) { const size_t sz = macaroon_serialize_size_hint(M); size_t i; unsigned char* tmp = NULL; unsigned char* ptr = NULL; int rc = 0; if (data_sz < sz) { *err = MACAROON_BUF_TOO_SMALL; return -1; } tmp = malloc(sizeof(unsigned char) * sz); if (!tmp) { *err = MACAROON_OUT_OF_MEMORY; return -1; } ptr = tmp; ptr = serialize_packet(&M->location, ptr); ptr = serialize_packet(&M->identifier, ptr); for (i = 0; i < M->num_caveats; ++i) { if (M->caveats[i].cid.size) { ptr = serialize_packet(&M->caveats[i].cid, ptr); } if (M->caveats[i].vid.size) { ptr = serialize_packet(&M->caveats[i].vid, ptr); } if (M->caveats[i].cl.size) { ptr = serialize_packet(&M->caveats[i].cl, ptr); } } ptr = serialize_packet(&M->signature, ptr); rc = b64_ntop(tmp, ptr - tmp, data, data_sz); free(tmp); if (rc < 0) { *err = MACAROON_BUF_TOO_SMALL; return -1; } return 0; } #ifdef MACAROONS_JSON_SUPPORT MACAROON_API size_t macaroon_serialize_json_size_hint(const struct macaroon* M) { /* the inner size hint captures the length of each packet, which for * kv-packets are the key, payload, and a little extra. */ size_t sz = macaroon_inner_size_hint_ascii(M); /* we then add the overheads imposed by JSON */ /* every key-value pair has some quotes, colons, commas, spacing */ sz += (4 + M->num_caveats * 3) * 8; /* every caveat is an object */ sz += M->num_caveats * 2; /* there's some overhead for the list and object notation */ sz += 4; /* finally, we're b64'ing this, so account for that */ sz = encoded_size(ENCODING_BASE64, sz); return sz; } static int json_help_add_strings(struct json_object* obj, const char* key, const char* val, size_t val_sz, enum macaroon_returncode* err) { struct json_object* jval = NULL; jval = json_object_new_string_len(val, val_sz); if (!jval) { *err = MACAROON_OUT_OF_MEMORY; return -1; } json_object_object_add(obj, key, jval); return 0; } static int json_help_add_kv_packet(struct json_object* obj, const struct packet* kv_pkt, enum encoding encoding, enum macaroon_returncode* err) { const unsigned char* enc_val = NULL; const unsigned char* key = NULL; const unsigned char* val = NULL; size_t enc_sz = 0; size_t key_sz = 0; size_t val_sz = 0; char* jkey = NULL; int rc = 0; if (parse_kv_packet(kv_pkt, &key, &key_sz, &val, &val_sz) < 0) { *err = MACAROON_INVALID; return -1; } if (encode(encoding, val, val_sz, &enc_val, &enc_sz, err) < 0) { return -1; } jkey = strndup((const char*)key, key_sz); if (!jkey) { *err = MACAROON_OUT_OF_MEMORY; return -1; } rc = json_help_add_strings(obj, jkey, (const char*)enc_val, enc_sz, err); free(jkey); if (enc_val != val) { free((void*)enc_val); } return rc; } MACAROON_API int macaroon_serialize_json(const struct macaroon* M, char* data, size_t data_sz, enum macaroon_returncode* err) { struct json_object* obj = NULL; struct json_object* arr = NULL; struct json_object* cav = NULL; const char* ser = NULL; size_t ser_sz = 0; size_t idx = 0; obj = json_object_new_object(); if (!obj) { *err = MACAROON_OUT_OF_MEMORY; return -1; } arr = json_object_new_array(); if (!arr) { json_object_put(obj); *err = MACAROON_OUT_OF_MEMORY; return -1; } json_object_object_add(obj, "caveats", arr); if (json_help_add_kv_packet(obj, &M->location, ENCODING_RAW, err) < 0 || json_help_add_kv_packet(obj, &M->identifier, ENCODING_RAW, err) < 0 || json_help_add_kv_packet(obj, &M->signature, ENCODING_HEX, err) < 0) { json_object_put(obj); return -1; } for (idx = 0; idx < M->num_caveats; ++idx) { cav = json_object_new_object(); if (!cav || json_object_array_add(arr, cav) < 0) { json_object_put(obj); *err = MACAROON_OUT_OF_MEMORY; return -1; } if ((M->caveats[idx].cid.size > 0 && json_help_add_kv_packet(cav, &M->caveats[idx].cid, ENCODING_RAW, err) < 0) || (M->caveats[idx].vid.size > 0 && json_help_add_kv_packet(cav, &M->caveats[idx].vid, ENCODING_BASE64, err) < 0) || (M->caveats[idx].cl.size > 0 && json_help_add_kv_packet(cav, &M->caveats[idx].cl, ENCODING_RAW, err) < 0)) { json_object_put(obj); return -1; } } ser = json_object_to_json_string_ext(obj, JSON_C_TO_STRING_PLAIN); ser_sz = strlen(ser); if (b64_ntop((const unsigned char*)ser, ser_sz, data, data_sz) < 0) { json_object_put(obj); *err = MACAROON_BUF_TOO_SMALL; return -1; } json_object_put(obj); return 0; } /* * decode decodes the given string, putting * the resulting data and size into result and result_sz. * On return, if *result != data, the caller is * responsible for freeing it. * * str is assumed to be null-terminated - str_sz * should not include the terminating zero. */ static int decode(enum encoding encoding, const unsigned char* str, size_t str_sz, const unsigned char** result, size_t* result_sz, enum macaroon_returncode* err) { unsigned char* dec_val; size_t dec_sz; switch (encoding) { case ENCODING_RAW: *result = str; *result_sz = str_sz; break; case ENCODING_BASE64: dec_val = malloc(str_sz); if (!dec_val) { *err = MACAROON_OUT_OF_MEMORY; return -1; } dec_sz = b64_pton(str, dec_val, str_sz); if (dec_sz <= 0) { *err = MACAROON_INVALID; free(dec_val); return -1; } *result = dec_val; *result_sz = dec_sz; break; case ENCODING_HEX: dec_sz = str_sz / 2; dec_val = malloc(dec_sz + 1); if (!dec_val) { *err = MACAROON_OUT_OF_MEMORY; return -1; } if (macaroon_hex2bin(str, str_sz, dec_val) < 0) { *err = MACAROON_INVALID; free(dec_val); return -1; } *result = dec_val; *result_sz = dec_sz; break; default: assert(0); } return 0; } static int json_help_copy_kv_packet(struct json_object* obj, const char* key, unsigned char* (*f)(const unsigned char*, size_t, struct packet*, unsigned char*), struct packet* pkt, enum encoding encoding, unsigned char** wptr, enum macaroon_returncode* err) { struct json_object* child = NULL; const unsigned char* str = NULL; const unsigned char* dec = NULL; size_t dec_sz; size_t str_sz = 0; if (!json_object_is_type(obj, json_type_object)) { *err = MACAROON_INVALID; return -1; } if (!json_object_object_get_ex(obj, key, &child) || !json_object_is_type(child, json_type_string)) { *err = MACAROON_INVALID; return -1; } str = (const unsigned char*)json_object_get_string(child); str_sz = json_object_get_string_len(child); if (decode(encoding, str, str_sz, &dec, &dec_sz, err) < 0) { return -1; } *wptr = f(dec, dec_sz, pkt, *wptr); if (dec != str) { free((void*)dec); } return 0; } static int json_help_copy_signature(struct json_object* obj, struct packet* pkt, unsigned char** wptr) { struct json_object* child = NULL; const char* str = NULL; size_t str_sz = 0; unsigned char sig[MACAROON_HASH_BYTES]; assert(json_object_is_type(obj, json_type_object)); if (!json_object_object_get_ex(obj, SIGNATURE, &child) || !json_object_is_type(child, json_type_string)) { return -1; } str = json_object_get_string(child); str_sz = json_object_get_string_len(child); if (str_sz != 2 * MACAROON_HASH_BYTES || macaroon_hex2bin(str, str_sz, sig) < 0) { return -1; } *wptr = create_signature_packet(sig, MACAROON_HASH_BYTES, pkt, *wptr); return 0; } MACAROON_API struct macaroon* macaroon_deserialize_json(const char* data, size_t data_sz, enum macaroon_returncode* err) { struct json_object* obj = NULL; struct json_object* arr = NULL; struct json_object* cav = NULL; struct json_tokener* tok = json_tokener_new(); size_t idx = 0; int arr_sz = 0; struct macaroon* M = NULL; unsigned char* ptr = NULL; if (!tok) { *err = MACAROON_OUT_OF_MEMORY; return NULL; } obj = json_tokener_parse_ex(tok, data, data_sz); if (!obj) { json_tokener_free(tok); *err = MACAROON_OUT_OF_MEMORY; return NULL; } if ((tok->char_offset < 0 || (size_t)tok->char_offset < data_sz) || (json_object_object_get_ex(obj, "caveats", &arr) != TRUE) || (json_object_get_type(arr) != json_type_array) || ((arr_sz = json_object_array_length(arr)) < 0)) { json_object_put(obj); json_tokener_free(tok); *err = MACAROON_INVALID; return NULL; } M = macaroon_malloc(arr_sz, data_sz, &ptr); if (!M) { json_object_put(obj); json_tokener_free(tok); *err = MACAROON_OUT_OF_MEMORY; return NULL; } if (json_help_copy_kv_packet(obj, LOCATION, create_location_packet, &M->location, ENCODING_RAW, &ptr, err) < 0 || json_help_copy_kv_packet(obj, IDENTIFIER, create_identifier_packet, &M->identifier, ENCODING_RAW, &ptr, err) < 0 || json_help_copy_kv_packet(obj, SIGNATURE, create_signature_packet, &M->signature, ENCODING_HEX, &ptr, err) < 0) { free(M); json_object_put(obj); json_tokener_free(tok); return NULL; } for (idx = 0; idx < (size_t)arr_sz; ++idx) { cav = json_object_array_get_idx(arr, idx); /* TODO deserialize caveat vid and location. */ if (!cav || !json_object_is_type(cav, json_type_object)) { free(M); json_object_put(obj); json_tokener_free(tok); *err = MACAROON_INVALID; return NULL; } if (json_help_copy_kv_packet(cav, CID, create_cid_packet, &M->caveats[idx].cid, ENCODING_RAW, &ptr, err) < 0) { free(M); json_object_put(obj); json_tokener_free(tok); return NULL; } } M->num_caveats = arr_sz; json_object_put(obj); json_tokener_free(tok); if (macaroon_validate(M) < 0) { free(M); *err = MACAROON_INVALID; return NULL; } return M; } #else /* MACAROONS_JSON_SUPPORT */ MACAROON_API size_t macaroon_serialize_json_size_hint(const struct macaroon* M) { (void) M; return 1; } MACAROON_API int macaroon_serialize_json(const struct macaroon* M, char* data, size_t data_sz, enum macaroon_returncode* err) { (void) M; (void) data; (void) data_sz; *err = MACAROON_NO_JSON_SUPPORT; return -1; } #endif /* MACAROONS_JSON_SUPPORT */ MACAROON_API struct macaroon* macaroon_deserialize(const char* _data, enum macaroon_returncode* err) { size_t num_pkts = 0; struct packet pkt = EMPTY_PACKET; const size_t _data_sz = strlen(_data); unsigned char* data = NULL; const unsigned char* end = NULL; const unsigned char* rptr = NULL; unsigned char* wptr = NULL; const unsigned char* tmp = NULL; const unsigned char* sig; const unsigned char* key; const unsigned char* val; size_t data_sz; size_t key_sz; size_t val_sz; int b64_sz; struct macaroon* M; data = malloc(sizeof(unsigned char) * _data_sz); if (!data) { *err = MACAROON_OUT_OF_MEMORY; return NULL; } b64_sz = b64_pton(_data, data, _data_sz); if (b64_sz <= 0) { *err = MACAROON_INVALID; free(data); return NULL; } #ifdef MACAROONS_JSON_SUPPORT if (data[0] == '{') { M = macaroon_deserialize_json((const char*)data, b64_sz, err); free(data); return M; } #else if (data[0] == '{') { *err = MACAROON_NO_JSON_SUPPORT; return NULL; } #endif data_sz = b64_sz; rptr = data; end = rptr + data_sz; while (rptr && rptr < end) { rptr = parse_packet(rptr, end, &pkt); ++num_pkts; } if (!rptr || num_pkts < 3) { *err = MACAROON_INVALID; free(data); return NULL; } assert(num_pkts < data_sz); M = macaroon_malloc((num_pkts - 3/*loc,id,sig*/), data_sz, &wptr); if (!M) { *err = MACAROON_OUT_OF_MEMORY; free(data); return NULL; } rptr = data; *err = MACAROON_INVALID; /* location */ if (copy_if_parses(&rptr, end, parse_location_packet, &M->location, &wptr) < 0) { free(M); free(data); return NULL; } /* identifier */ if (copy_if_parses(&rptr, end, parse_identifier_packet, &M->identifier, &wptr) < 0) { free(M); free(data); return NULL; } M->num_caveats = 0; while (1) { tmp = parse_packet(rptr, end, &pkt); if (parse_kv_packet(&pkt, &key, &key_sz, &val, &val_sz) < 0) { break; } if (key_sz == CID_SZ && memcmp(key, CID, CID_SZ) == 0) { if (M->caveats[M->num_caveats].cid.size) { ++M->num_caveats; } wptr = copy_packet(&pkt, &M->caveats[M->num_caveats].cid, wptr); } else if (key_sz == VID_SZ && memcmp(key, VID, VID_SZ) == 0) { if (M->caveats[M->num_caveats].vid.size) { free(M); free(data); return NULL; } wptr = copy_packet(&pkt, &M->caveats[M->num_caveats].vid, wptr); } else if (key_sz == CL_SZ && memcmp(key, CL, CL_SZ) == 0) { if (M->caveats[M->num_caveats].cl.size) { free(M); free(data); return NULL; } wptr = copy_packet(&pkt, &M->caveats[M->num_caveats].cl, wptr); } else { break; } /* advance to the next packet */ rptr = tmp; } /* catch the tail packet */ if (M->caveats[M->num_caveats].cid.size) { ++M->num_caveats; } /* signature */ rptr = parse_packet(rptr, end, &pkt); assert(rptr); if (parse_signature_packet(&pkt, &sig) < 0) { free(M); free(data); return NULL; } wptr = copy_packet(&pkt, &M->signature, wptr); if (macaroon_validate(M) < 0) { free(M); free(data); return NULL; } *err = MACAROON_SUCCESS; return M; } MACAROON_API size_t macaroon_inspect_size_hint(const struct macaroon* M) { /* TODO why the extra MACAROON_HASH_BYTES here? */ return macaroon_inner_size_hint_ascii(M) + MACAROON_HASH_BYTES; } static char* inspect_packet(const struct packet* from, enum encoding encoding, char* ptr, char* ptr_end, enum macaroon_returncode *err) { const unsigned char* key = NULL; const unsigned char* val = NULL; const unsigned char* enc_val = NULL; size_t key_sz = 0; size_t val_sz = 0; size_t enc_sz = 0; size_t total_sz = 0; int rc; rc = parse_kv_packet(from, &key, &key_sz, &val, &val_sz); assert(rc == 0); if (encode(encoding, val, val_sz, &enc_val, &enc_sz, err) < 0) { return NULL; } total_sz = key_sz + 1 + enc_sz + 1; assert(ptr_end >= ptr); assert(total_sz <= (size_t)(ptr_end - ptr)); memmove(ptr, key, key_sz); ptr[key_sz] = ' '; memmove(ptr + key_sz + 1, enc_val, enc_sz); ptr[key_sz + 1 + enc_sz] = '\n'; if (enc_val != val) { free((void *)enc_val); } return ptr + total_sz; } MACAROON_API int macaroon_inspect(const struct macaroon* M, char* data, size_t data_sz, enum macaroon_returncode* err) { const size_t sz = macaroon_inspect_size_hint(M); size_t i = 0; char* ptr = data; char* ptr_end = data + data_sz; if (data_sz < sz) { *err = MACAROON_BUF_TOO_SMALL; return -1; } ptr = inspect_packet(&M->location, ENCODING_RAW, ptr, ptr_end, err); if (ptr == NULL) { return -1; } ptr = inspect_packet(&M->identifier, ENCODING_RAW, ptr, ptr_end, err); if (ptr == NULL) { return -1; } for (i = 0; i < M->num_caveats; ++i) { if (M->caveats[i].cid.size) { ptr = inspect_packet(&M->caveats[i].cid, ENCODING_RAW, ptr, ptr_end, err); if (ptr == NULL) { return -1; } } if (M->caveats[i].vid.size) { ptr = inspect_packet(&M->caveats[i].vid, ENCODING_BASE64, ptr, ptr_end, err); if (ptr == NULL) { return -1; } } if (M->caveats[i].cl.size) { ptr = inspect_packet(&M->caveats[i].cl, ENCODING_RAW, ptr, ptr_end, err); if (ptr == NULL) { return -1; } } } ptr = inspect_packet(&M->signature, ENCODING_HEX, ptr, ptr_end, err); if (ptr == NULL) { return -1; } /* Replace final newline with terminator. */ ptr[-1] = '\0'; return 0; } MACAROON_API struct macaroon* macaroon_copy(const struct macaroon* N, enum macaroon_returncode* err) { size_t i; size_t sz; struct macaroon* M; unsigned char* ptr; assert(N); VALIDATE(N); sz = macaroon_body_size(N); sz += PACKET_SIZE(SIGNATURE, MACAROON_HASH_BYTES); M = macaroon_malloc(N->num_caveats + 1, sz, &ptr); if (!M) { *err = MACAROON_OUT_OF_MEMORY; return NULL; } M->num_caveats = N->num_caveats; ptr = copy_packet(&N->location, &M->location, ptr); ptr = copy_packet(&N->identifier, &M->identifier, ptr); for (i = 0; i < N->num_caveats; ++i) { ptr = copy_packet(&N->caveats[i].cid, &M->caveats[i].cid, ptr); ptr = copy_packet(&N->caveats[i].vid, &M->caveats[i].vid, ptr); ptr = copy_packet(&N->caveats[i].cl, &M->caveats[i].cl, ptr); } ptr = copy_packet(&N->signature, &M->signature, ptr); VALIDATE(M); return M; } MACAROON_API int macaroon_cmp(const struct macaroon* M, const struct macaroon* N) { size_t i = 0; size_t num_caveats = 0; unsigned long long ret = 0; assert(M); assert(N); VALIDATE(M); VALIDATE(N); ret |= M->num_caveats ^ N->num_caveats; ret |= packet_cmp(&M->location, &N->location); ret |= packet_cmp(&M->identifier, &N->identifier); ret |= packet_cmp(&M->signature, &N->signature); num_caveats = M->num_caveats < N->num_caveats ? M->num_caveats : N->num_caveats; for (i = 0; i < num_caveats; ++i) { ret |= packet_cmp(&M->caveats[i].cid, &N->caveats[i].cid); ret |= packet_cmp(&M->caveats[i].vid, &N->caveats[i].vid); ret |= packet_cmp(&M->caveats[i].cl, &N->caveats[i].cl); } return ret; } libmacaroons-releases-0.3.0/macaroons.h000066400000000000000000000177111253111166600201300ustar00rootroot00000000000000/* Copyright (c) 2014, 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. */ #ifndef macaroons_h_ #define macaroons_h_ /* C */ #include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* All byte strings must be less than this length. * Enforced via "assert" internally. * */ #define MACAROON_MAX_STRLEN 32768 /* Place a sane limit on the number of caveats */ #define MACAROON_MAX_CAVEATS 65536 /* Recommended secret length */ #define MACAROON_SUGGESTED_SECRET_LENGTH 32 /* Opaque type whose internals are private to libmacaroons */ struct macaroon; struct macaroon_verifier; enum macaroon_returncode { MACAROON_SUCCESS = 2048, MACAROON_OUT_OF_MEMORY = 2049, MACAROON_HASH_FAILED = 2050, MACAROON_INVALID = 2051, MACAROON_TOO_MANY_CAVEATS = 2052, MACAROON_CYCLE = 2053, MACAROON_BUF_TOO_SMALL = 2054, MACAROON_NOT_AUTHORIZED = 2055, MACAROON_NO_JSON_SUPPORT = 2056 }; /* Create a new macaroon. * - location/location_sz is a hint to the target's location * - key/key_sz is the key used as a secret for macaroon construction * - id/id_sz is the public identifier the macaroon issuer can use to identify * the key */ struct macaroon* macaroon_create(const unsigned char* location, size_t location_sz, const unsigned char* key, size_t key_sz, const unsigned char* id, size_t id_sz, enum macaroon_returncode* err); /* Destroy a macaroon, freeing resources */ void macaroon_destroy(struct macaroon* M); /* Check a macaroon's integrity * * This routine is used internally, and is exposed as part of the public API for * use in assert() statements. * * 0 -> all good * !0 -> no good */ int macaroon_validate(const struct macaroon* M); /* Add a new first party caveat, and return a new macaroon. * - predicate/predicate_sz is the caveat to be added to the macaroon * * Returns a new macaroon, leaving the original untouched. */ struct macaroon* macaroon_add_first_party_caveat(const struct macaroon* M, const unsigned char* predicate, size_t predicate_sz, enum macaroon_returncode* err); /* Add a new third party caveat, and return a new macaroon. * - location/location_sz is a hint to the third party's location * - key/keys_sz is a secret shared shared between this macaroon and the third * party. Guard it as carefully as you do the key for macaroon_create. * - id/id_sz is the identifier for this macaroon. If presented to the third * party, the third party must be able to recall the secret and predicate to * check. * A good way to generate this ID is to generate N random bytes as the key, * and encrypt these bytes and the caveat. Pass the bytes and N as the key, * and pass the ciphertext as the ID. * * Returns a new macaroon, leaving the original untouched. */ struct macaroon* macaroon_add_third_party_caveat(const struct macaroon* M, const unsigned char* location, size_t location_sz, const unsigned char* key, size_t key_sz, const unsigned char* id, size_t id_sz, enum macaroon_returncode* err); /* Where are the third-parties that give discharge macaroons? */ unsigned macaroon_num_third_party_caveats(const struct macaroon* M); int macaroon_third_party_caveat(const struct macaroon* M, unsigned which, const unsigned char** location, size_t* location_sz, const unsigned char** identifier, size_t* identifier_sz); /* Prepare the macaroon for a request */ struct macaroon* macaroon_prepare_for_request(const struct macaroon* M, const struct macaroon* D, enum macaroon_returncode* err); /* Verification tool for verifying macaroons */ struct macaroon_verifier* macaroon_verifier_create(); void macaroon_verifier_destroy(struct macaroon_verifier* V); int macaroon_verifier_satisfy_exact(struct macaroon_verifier* V, const unsigned char* predicate, size_t predicate_sz, enum macaroon_returncode* err); int macaroon_verifier_satisfy_general(struct macaroon_verifier* V, int (*general_check)(void* f, const unsigned char* pred, size_t pred_sz), void* f, enum macaroon_returncode* err); int macaroon_verify(const struct macaroon_verifier* V, const struct macaroon* M, const unsigned char* key, size_t key_sz, struct macaroon** MS, size_t MS_sz, enum macaroon_returncode* err); /* Access routines for the macaroon */ void macaroon_location(const struct macaroon* M, const unsigned char** location, size_t* location_sz); void macaroon_identifier(const struct macaroon* M, const unsigned char** identifier, size_t* identifier_sz); void macaroon_signature(const struct macaroon* M, const unsigned char** signature, size_t* signature_sz); /* Serialize and deserialize macaroons */ size_t macaroon_serialize_size_hint(const struct macaroon* M); int macaroon_serialize(const struct macaroon* M, char* data, size_t data_sz, enum macaroon_returncode* err); size_t macaroon_serialize_json_size_hint(const struct macaroon* M); int macaroon_serialize_json(const struct macaroon* M, char* data, size_t data_sz, enum macaroon_returncode* err); struct macaroon* macaroon_deserialize_json(const char* data, size_t data_sz, enum macaroon_returncode* err); struct macaroon* macaroon_deserialize(const char* data, enum macaroon_returncode* err); /* Human-readable representation *FOR DEBUGGING ONLY* */ size_t macaroon_inspect_size_hint(const struct macaroon* M); int macaroon_inspect(const struct macaroon* M, char* data, size_t data_sz, enum macaroon_returncode* err); /* Utilities for manipulating and comparing macaroons */ /* allocate a new copy of the macaroon */ struct macaroon* macaroon_copy(const struct macaroon* M, enum macaroon_returncode* err); /* 0 if equal; !0 if non-equal; no other comparison implied */ int macaroon_cmp(const struct macaroon* M, const struct macaroon* N); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* macaroons_h_ */ libmacaroons-releases-0.3.0/packet.c000066400000000000000000000234511253111166600174060ustar00rootroot00000000000000/* Copyright (c) 2014, 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* c */ #include #include #include /* macaroons */ #include "constants.h" #include "packet.h" #include "port.h" static unsigned char* packet_memmove(unsigned char* ptr, const unsigned char* src, size_t sz) { memmove(ptr, src, sz); return ptr + sz; } static unsigned char* packet_header(size_t sz, unsigned char* ptr) { static const char hex[] = "0123456789abcdef"; assert(sz < 65536); ptr[0] = hex[(sz >> 12) & 15]; ptr[1] = hex[(sz >> 8) & 15]; ptr[2] = hex[(sz >> 4) & 15]; ptr[3] = hex[(sz) & 15]; assert(PACKET_PREFIX == 4); /* modify above on failure */ return ptr + PACKET_PREFIX; } const unsigned char* parse_packet(const unsigned char* ptr, const unsigned char* const end, struct packet* pkt) { static const char hex[] = "0123456789abcdef"; const char* tmp; uint32_t sz = 0; int i = 0; if (end - ptr < PACKET_PREFIX) { return NULL; } for (i = 0; i < PACKET_PREFIX; ++i) { sz <<= 4; tmp = strchr(hex, ptr[i]); if (!tmp) { return NULL; } sz |= tmp - hex; } if (end - ptr < sz) { return NULL; } pkt->data = ptr; pkt->size = sz; return ptr + sz; } int packet_cmp(const struct packet* lhs, const struct packet* rhs) { return macaroon_memcmp(lhs->data, rhs->data, (lhs->size < rhs->size) ? lhs->size : rhs->size); } unsigned char* create_kv_packet(const unsigned char* key, size_t key_sz, const unsigned char* val, size_t val_sz, struct packet* pkt, unsigned char* ptr) { size_t sz = PACKET_PREFIX + 2 + key_sz + val_sz; pkt->data = ptr; pkt->size = sz; memset(ptr, 0, sz); ptr = packet_header(sz, ptr); ptr = packet_memmove(ptr, key, key_sz); *ptr = ' '; ++ptr; ptr = packet_memmove(ptr, val, val_sz); *ptr = '\n'; ++ptr; return ptr; } int parse_kv_packet(const struct packet* pkt, const unsigned char** key, size_t* key_sz, const unsigned char** val, size_t* val_sz) { unsigned char* tmp = NULL; unsigned char prefix[PACKET_PREFIX]; *key = NULL; *key_sz = 0; *val = NULL; *val_sz = 0; if (pkt->size > PACKET_MAX_SIZE) { return -1; } packet_header(pkt->size, prefix); if (pkt->size < PACKET_PREFIX + 2 || memcmp(pkt->data, prefix, PACKET_PREFIX) != 0 || pkt->data[pkt->size - 1] != '\n') { return -1; } tmp = memchr(pkt->data + PACKET_PREFIX, ' ', pkt->size - PACKET_PREFIX); if (!tmp) { return -1; } *key = pkt->data + PACKET_PREFIX; *key_sz = tmp - *key; *val = tmp + 1; *val_sz = pkt->size - PACKET_PREFIX - 2 - *key_sz; return 0; } unsigned char* copy_packet(const struct packet* from, struct packet* to, unsigned char* ptr) { memmove(ptr, from->data, from->size); to->data = ptr; to->size = from->size; return ptr + to->size; } unsigned char* serialize_packet(const struct packet* from, unsigned char* ptr) { memmove(ptr, from->data, from->size); return ptr + from->size; } int copy_if_parses(const unsigned char** rptr, const unsigned char* const end, int (*f)(const struct packet* pkt, const unsigned char** s, size_t* s_sz), struct packet* to, unsigned char** wptr) { const unsigned char* tmp; size_t tmp_sz; struct packet pkt; *rptr = parse_packet(*rptr, end, &pkt); if (!*rptr || f(&pkt, &tmp, &tmp_sz) < 0) { return -1; } *wptr = copy_packet(&pkt, to, *wptr); return 0; } unsigned char* create_location_packet(const unsigned char* location, size_t location_sz, struct packet* pkt, unsigned char* ptr) { return create_kv_packet((const unsigned char*)LOCATION, LOCATION_SZ, location, location_sz, pkt, ptr); } int parse_location_packet(const struct packet* packet, const unsigned char** location, size_t* location_sz) { const unsigned char* key; const unsigned char* val; size_t key_sz; size_t val_sz; if (parse_kv_packet(packet, &key, &key_sz, &val, &val_sz) < 0) { return -1; } if (key_sz != LOCATION_SZ || memcmp(key, LOCATION, LOCATION_SZ) != 0) { return -1; } *location = val; *location_sz = val_sz; return 0; } unsigned char* create_identifier_packet(const unsigned char* identifier, size_t identifier_sz, struct packet* pkt, unsigned char* ptr) { return create_kv_packet((const unsigned char*)IDENTIFIER, IDENTIFIER_SZ, identifier, identifier_sz, pkt, ptr); } int parse_identifier_packet(const struct packet* packet, const unsigned char** identifier, size_t* identifier_sz) { const unsigned char* key; const unsigned char* val; size_t key_sz; size_t val_sz; if (parse_kv_packet(packet, &key, &key_sz, &val, &val_sz) < 0) { return -1; } if (key_sz != IDENTIFIER_SZ || memcmp(key, IDENTIFIER, IDENTIFIER_SZ) != 0) { return -1; } *identifier = val; *identifier_sz = val_sz; return 0; } unsigned char* create_signature_packet(const unsigned char* signature, size_t signature_sz, struct packet* pkt, unsigned char* ptr) { return create_kv_packet((const unsigned char*)SIGNATURE, SIGNATURE_SZ, signature, signature_sz, pkt, ptr); } int parse_signature_packet(const struct packet* packet, const unsigned char** signature) { const unsigned char* key; const unsigned char* val; size_t key_sz; size_t val_sz; if (parse_kv_packet(packet, &key, &key_sz, &val, &val_sz) < 0) { return -1; } if (key_sz != SIGNATURE_SZ || val_sz != MACAROON_HASH_BYTES || memcmp(key, SIGNATURE, SIGNATURE_SZ) != 0) { return -1; } *signature = val; return 0; } unsigned char* create_cid_packet(const unsigned char* cid, size_t cid_sz, struct packet* pkt, unsigned char* ptr) { return create_kv_packet((const unsigned char*)CID, CID_SZ, cid, cid_sz, pkt, ptr); } int parse_cid_packet(const struct packet* packet, const unsigned char** cid, size_t* cid_sz) { const unsigned char* key; const unsigned char* val; size_t key_sz; size_t val_sz; if (parse_kv_packet(packet, &key, &key_sz, &val, &val_sz) < 0) { return -1; } if (key_sz != CID_SZ || memcmp(key, CID, CID_SZ) != 0) { return -1; } *cid = val; *cid_sz = val_sz; return 0; } unsigned char* create_vid_packet(const unsigned char* vid, size_t vid_sz, struct packet* pkt, unsigned char* ptr) { return create_kv_packet((const unsigned char*)VID, VID_SZ, vid, vid_sz, pkt, ptr); } int parse_vid_packet(const struct packet* packet, const unsigned char** vid, size_t* vid_sz) { const unsigned char* key; const unsigned char* val; size_t key_sz; size_t val_sz; if (parse_kv_packet(packet, &key, &key_sz, &val, &val_sz) < 0) { return -1; } if (key_sz != VID_SZ || memcmp(key, VID, VID_SZ) != 0) { return -1; } *vid = val; *vid_sz = val_sz; return 0; } unsigned char* create_cl_packet(const unsigned char* cl, size_t cl_sz, struct packet* pkt, unsigned char* ptr) { return create_kv_packet((const unsigned char*)CL, CL_SZ, cl, cl_sz, pkt, ptr); } int parse_cl_packet(const struct packet* packet, const unsigned char** cl, size_t* cl_sz) { const unsigned char* key; const unsigned char* val; size_t key_sz; size_t val_sz; if (parse_kv_packet(packet, &key, &key_sz, &val, &val_sz) < 0) { return -1; } if (key_sz != CL_SZ || memcmp(key, CL, CL_SZ) != 0) { return -1; } *cl = val; *cl_sz = val_sz; return 0; } libmacaroons-releases-0.3.0/packet.h000066400000000000000000000115241253111166600174110ustar00rootroot00000000000000/* Copyright (c) 2014, 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. */ #ifndef macaroons_packet_h_ #define macaroons_packet_h_ /* c */ #include #define PACKET_PREFIX 4 #define PACKET_SIZE(KEY, VAL) (PACKET_PREFIX + STRLENOF(KEY) + (VAL) + 2) #define PACKET_MAX_SIZE 65535 #define NULL_PACKET {NULL, 0}; #define EMPTY_PACKET {(const unsigned char*)"0004", 4} struct packet { const unsigned char* data; size_t size; }; const unsigned char* parse_packet(const unsigned char* ptr, const unsigned char* const end, struct packet* pkt); /* 0 if equal; !0 if non-equal; no other comparison implied */ int packet_cmp(const struct packet* lhs, const struct packet* rhs); /* A key-value packet has this form: * <4 byte header> */ unsigned char* create_kv_packet(const unsigned char* key, size_t key_sz, const unsigned char* val, size_t val_sz, struct packet* pkt, unsigned char* ptr); int parse_kv_packet(const struct packet* pkt, const unsigned char** key, size_t* key_sz, const unsigned char** val, size_t* val_sz); /* copy a packet to the memory pointed to by ptr */ unsigned char* copy_packet(const struct packet* from, struct packet* to, unsigned char* ptr); unsigned char* serialize_packet(const struct packet* from, unsigned char* ptr); int copy_if_parses(const unsigned char** rptr, const unsigned char* const end, int (*f)(const struct packet* pkt, const unsigned char** s, size_t* s_sz), struct packet* to, unsigned char** wptr); /* create a location packet */ unsigned char* create_location_packet(const unsigned char* location, size_t location_sz, struct packet* pkt, unsigned char* ptr); int parse_location_packet(const struct packet* pkt, const unsigned char** location, size_t* location_sz); /* create a identifier packet */ unsigned char* create_identifier_packet(const unsigned char* identifier, size_t identifier_sz, struct packet* pkt, unsigned char* ptr); int parse_identifier_packet(const struct packet* pkt, const unsigned char** identifier, size_t* identifier_sz); /* create a signature packet */ unsigned char* create_signature_packet(const unsigned char* signature, size_t sig_sz, struct packet* pkt, unsigned char* ptr); int parse_signature_packet(const struct packet* pkt, const unsigned char** signature); /* caveat packets */ unsigned char* create_cid_packet(const unsigned char* cid, size_t cid_sz, struct packet* pkt, unsigned char* ptr); int parse_cid_packet(const struct packet* pkt, const unsigned char** cid, size_t* cid_sz); unsigned char* create_vid_packet(const unsigned char* vid, size_t vid_sz, struct packet* pkt, unsigned char* ptr); int parse_vid_packet(const struct packet* pkt, const unsigned char** vid, size_t* vid_sz); unsigned char* create_cl_packet(const unsigned char* cl, size_t cl_sz, struct packet* pkt, unsigned char* ptr); int parse_cl_packet(const struct packet* pkt, const unsigned char** cl, size_t* cl_sz); #endif /* macaroons_packet_h_ */ libmacaroons-releases-0.3.0/port.c000066400000000000000000000121051253111166600171150ustar00rootroot00000000000000/* Copyright (c) 2014, 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* C */ #include #include /* sodium */ #include #include #include #include /* macaroons */ #include "port.h" #if crypto_auth_hmacsha256_BYTES != MACAROON_HASH_BYTES #error set your constants right #endif #if crypto_secretbox_xsalsa20poly1305_KEYBYTES != MACAROON_SECRET_KEY_BYTES #error set your constants right #endif #if crypto_secretbox_xsalsa20poly1305_NONCEBYTES != MACAROON_SECRET_NONCE_BYTES #error set your constants right #endif #if crypto_secretbox_xsalsa20poly1305_ZEROBYTES != MACAROON_SECRET_TEXT_ZERO_BYTES #error set your constants right #endif #if crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES != MACAROON_SECRET_BOX_ZERO_BYTES #error set your constants right #endif /* So why this port file? Why add a level of indirection? It makes the API * consistent with the coding style throughout the rest of the code. A reader * familiar with the macaroons code can intuitively follow the primitives, * without needing to understand the sodium API. * * As a bonus, it makes it ridiculously easy to swap out sodium for something a * little more hipstery, like TweetNACL if that's your thing. */ void macaroon_memzero(void* data, size_t data_sz) { sodium_memzero(data, data_sz); } int macaroon_memcmp(const void* data1, const void* data2, size_t data_sz) { return sodium_memcmp(data1, data2, data_sz); } int macaroon_randombytes(void* data, const size_t data_sz) { randombytes_buf(data, data_sz); return 0; } int macaroon_hmac(const unsigned char* _key, size_t _key_sz, const unsigned char* text, size_t text_sz, unsigned char* hash) { int rc; unsigned char key[crypto_auth_hmacsha256_KEYBYTES]; sodium_memzero(key, crypto_auth_hmacsha256_BYTES); memmove(key, _key, _key_sz < sizeof(key) ? _key_sz : sizeof(key)); rc = crypto_auth_hmacsha256(hash, text, text_sz, key); assert(rc == 0); return 0; } int macaroon_secretbox(const unsigned char* enc_key, const unsigned char* enc_nonce, const unsigned char* plaintext, size_t plaintext_sz, unsigned char* ciphertext) { return crypto_secretbox_xsalsa20poly1305(ciphertext, plaintext, plaintext_sz, enc_nonce, enc_key); } int macaroon_secretbox_open(const unsigned char* enc_key, const unsigned char* enc_nonce, const unsigned char* ciphertext, size_t ciphertext_sz, unsigned char* plaintext) { return crypto_secretbox_xsalsa20poly1305_open(plaintext, ciphertext, ciphertext_sz, enc_nonce, enc_key); } void macaroon_bin2hex(const unsigned char* bin, size_t bin_sz, char* hex) { void* ptr = sodium_bin2hex(hex, bin_sz * 2, bin, bin_sz); assert(ptr == hex); } int macaroon_hex2bin(const char* hex, size_t hex_sz, unsigned char* bin) { size_t idx = 0; static const char bet[] = "0123456789abcdef"; const char* tmp = NULL; unsigned byte; if(hex_sz & 1) { return -1; } for (idx = 0; idx < hex_sz; idx += 2) { byte = 0; tmp = strchr(bet, hex[idx]); if (!tmp) { return -1; } byte |= tmp - bet; byte <<= 4; tmp = strchr(bet, hex[idx + 1]); if (!tmp) { return -1; } byte |= tmp - bet; bin[idx >> 1] = byte & 0xffU; } return 0; } libmacaroons-releases-0.3.0/port.h000066400000000000000000000057101253111166600171260ustar00rootroot00000000000000/* Copyright (c) 2014, 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. */ #ifndef macaroons_port_h_ #define macaroons_port_h_ #define MACAROON_HASH_BYTES 32U #define MACAROON_SECRET_KEY_BYTES 32U #define MACAROON_SECRET_NONCE_BYTES 24U /* * The number of zero bytes required by crypto_secretbox * before the plaintext. */ #define MACAROON_SECRET_TEXT_ZERO_BYTES 32U /* * The number of zero bytes placed by crypto_secretbox * before the ciphertext */ #define MACAROON_SECRET_BOX_ZERO_BYTES 16U void macaroon_memzero(void* data, size_t data_sz); int macaroon_memcmp(const void* data1, const void* data2, size_t data_sz); int macaroon_randombytes(void* data, const size_t data_sz); int macaroon_hmac(const unsigned char* key, size_t key_sz, const unsigned char* text, size_t text_sz, unsigned char* hash); int macaroon_secretbox(const unsigned char* enc_key, const unsigned char* enc_nonce, const unsigned char* plaintext, size_t plaintext_sz, unsigned char* ciphertext); int macaroon_secretbox_open(const unsigned char* enc_key, const unsigned char* enc_nonce, const unsigned char* ciphertext, size_t ciphertext_sz, unsigned char* plaintext); void macaroon_bin2hex(const unsigned char* bin, size_t bin_sz, char* hex); int macaroon_hex2bin(const char* hex, size_t hex_sz, unsigned char* bin); #endif /* macaroons_port_h_ */ libmacaroons-releases-0.3.0/test/000077500000000000000000000000001253111166600167455ustar00rootroot00000000000000libmacaroons-releases-0.3.0/test/env.sh000066400000000000000000000002511253111166600200670ustar00rootroot00000000000000export MACAROONS_SRCDIR="$1" export MACAROONS_BUILDDIR="$2" export MACAROONS_VERSION="$3" export PYTHONPATH="${MACAROONS_BUILDDIR}"/bindings/python/.libs:${PYTHONPATH} libmacaroons-releases-0.3.0/test/python-hmac-sanity-check000066400000000000000000000027241253111166600235040ustar00rootroot00000000000000Python HMAC Sanity Check ======================== This library uses the libsodium routines by default for the C library. This blurb walks through the first few steps of the README tutorial and checks that the signatures are the same when computed using the Python HMAC module. We hard code the signatures here, so changes to the implementation or README will not be reflected here. If you change either, you'll likely want to update this too. >>> secret = 'this is our super secret key; only we should know it' >>> public = 'we used our secret key' >>> import hashlib >>> import hmac >>> key = secret + '\x00' * (32 - len(secret)) >>> key = key[:32] >>> key 'this is our super secret key; on' >>> x = hmac.HMAC(key, digestmod=hashlib.sha256) >>> x.update(public) >>> x.hexdigest() 'c60b4b3540bb1b2f2ef28d1c895691cc4a5e07a38a9d3b1c3379fb485293372f' >>> key = x.hexdigest().decode('hex') >>> x = hmac.HMAC(key, digestmod=hashlib.sha256) >>> x.update('account = 3735928559') >>> x.hexdigest() '5c933dc9a7d036dfcd1740b4f26d737397a1ff635eac900f3226973503caaaa5' >>> key = x.hexdigest().decode('hex') >>> x = hmac.HMAC(key, digestmod=hashlib.sha256) >>> x.update('time < 2015-01-01T00:00') >>> x.hexdigest() '7a559b20c8b607009ebce138c200585e9d0deca6d23b3ead6c5e0ba6861d3858' >>> key = x.hexdigest().decode('hex') >>> x = hmac.HMAC(key, digestmod=hashlib.sha256) >>> x.update('email = alice@example.org') >>> x.hexdigest() 'e42bbb02a9a5a303483cb6295c497ae51ad1d5cb10003cbe548d907e7e62f5e4' libmacaroons-releases-0.3.0/test/python-hmac-sanity-check.sh000077500000000000000000000001241253111166600241100ustar00rootroot00000000000000#!/bin/bash python2 -m doctest "${MACAROONS_SRCDIR}/test/python-hmac-sanity-check" libmacaroons-releases-0.3.0/test/readme.sh000077500000000000000000000000751253111166600205430ustar00rootroot00000000000000#!/bin/bash python2 -m doctest "${MACAROONS_SRCDIR}/README"