jmacaroons


Source link: https://github.com/nitram509/jmacaroons

Macaroons are Better Than Cookies!

This Java 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!

This project started as a port of libmacaroons [2] library. The primary goals are

  • being compatible to libmacaroons
  • having no external dependencies, except the Java Runtime
  • being Android compatible, while using Java6
  • focus on binary serialization format (currently, JSON format isn't supported)
  • being the reference implementation in the Java community ;-)

There is a playground (testing environment) available, where you can build and verify macaroons online.

License

Usage/Import In Your Project

This library jmacaroons is available via Maven Central.

Maven

<dependency>
<groupId>com.github.nitram509</groupId>
<artifactId>jmacaroons</artifactId>
<version>0.3.1</version> </dependency>

Gradle

compile 'com.github.nitram509:jmacaroons:0.3.1'

Build Status

Community & Badges

Listed on Android Arsenal:

If you like this project, endorse please:

Creating Your First Macaroon

Lets create a simple macaroon

String location = "http://www.example.org"; String secretKey = "this is our super secret key; only we should know it"; String identifier = "we used our secret key"; Macaroon macaroon = MacaroonsBuilder.create(location, secretKey, identifier);

Of course, this macaroon can be displayed in a more human-readable form for easy debugging

System.out.println(macaroon.inspect());
  // > location http://www.example.org // > identifier we used our secret key // > signature e3d9e02908526c4c0039ae15114115d97fdd68bf2ba379b342aaf0f617d0552f

Serializing

Macaroons are serialized, using Base64 URL safe encoding RFC 4648. This way you can very easily append it to query string within URIs.

String serialized = macaroon.serialize();
 System.out.println("Serialized: " + serialized);
Serialized: MDAyNGxvY2F0aW9uIGh0dHA6Ly93d3cuZXhhbXBsZS5vcmcKMDAyNmlkZW50aWZpZXIgd2UgdXNlZCBvdXIgc2VjcmV0IGtleQowMDJmc2lnbmF0dXJlIOPZ4CkIUmxMADmuFRFBFdl_3Wi_K6N5s0Kq8PYX0FUvCg 

Note: Base64 URL safe is supported since v0.3.0. jmacaroons also de-serializes regular Base64 to maintain backward compatibility.

De-Serializing

Macaroon macaroon = MacaroonsBuilder.deserialize(serialized);
 System.out.println(macaroon.inspect());
  // > location http://www.example.org // > identifier we used our secret key // > signature e3d9e02908526c4c0039ae15114115d97fdd68bf2ba379b342aaf0f617d0552f

Verifying Your Macaroon

A verifier can only ever successfully verify a macaroon when provided with the macaroon and its corresponding secret - no secret, no authorization.

MacaroonsVerifier verifier = new MacaroonsVerifier(macaroon);
 String secret = "this is our super secret key; only we should know it"; boolean valid = verifier.isValid(secret);
  // > True

Adding Caveats

When creating a new macaroon, you can add a caveat to our macaroon that restricts it to just the account number 3735928559.

String location = "http://www.example.org"; String secretKey = "this is our super secret key; only we should know it"; String identifier = "we used our secret key"; Macaroon macaroon = new MacaroonsBuilder(location, secretKey, identifier)
  .add_first_party_caveat("account = 3735928559")
  .getMacaroon();

Because macaroon objects are immutable, they have to be modified via MacaroonsBuilder. Thus, a new macaroon object will be created.

Macaroon macaroon = MacaroonsBuilder.modify(macaroon)
  .add_first_party_caveat("account = 3735928559")
  .getMacaroon();
 System.out.println(macaroon.inspect());
  // > location http://www.example.org // > identifier we used our secret key // > cid account = 3735928559 // > signature 1efe4763f290dbce0c1d08477367e11f4eee456a64933cf662d79772dbb82128

Verifying Macaroons With Caveats

The verifier should say that this macaroon is unauthorized because the verifier cannot prove that the caveat (account = 3735928559) is satisfied. We can see that it fails just as we would expect.

String location = "http://www.example.org"; String secretKey = "this is our super secret key; only we should know it"; String identifier = "we used our secret key"; Macaroon macaroon = new MacaroonsBuilder(location, secretKey, identifier)
  .add_first_party_caveat("account = 3735928559")
  .getMacaroon();
 MacaroonsVerifier verifier = new MacaroonsVerifier(macaroon);
 verifier.isValid(secretKey);
 // > False

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 "satisfyExact()". When it finds a match, it knows that the caveat holds and it can move onto the next caveat in the macaroon.

verifier.satisfyExact("account = 3735928559");
 verifier.isValid(secretKey);
 // > True

The verifier can be made more general, and be "future-proofed", so that it will still function correctly even if somehow the authorization policy changes; for example, by adding the three following facts, the verifier will continue to work even if someone decides to self-attenuate itself macaroons to be only usable from IP address and browser:

verifier.satisfyExact("IP = 127.0.0.1')");
 verifier.satisfyExact("browser = Chrome')");
 verifier.isValid(secretKey);
 // > True

There is also a more general way to check caveats, via callbacks. When providing such a callback to the verifier, it is able to check if the caveat satisfies special constrains.

Macaroon macaroon = new MacaroonsBuilder(location, secretKey, identifier)
  .add_first_party_caveat("time < 2042-01-01T00:00")
  .getMacaroon();
 MacaroonsVerifier verifier = new MacaroonsVerifier(macaroon);
 verifier.isValid(secretKey);
 // > False  verifier.satisfyGeneral(new TimestampCaveatVerifier());
 verifier.isValid(secretKey);
 // > True

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.

// create a simple macaroon first String location = "http://mybank/"; String secret = "this is a different super-secret key; never use the same secret twice"; String publicIdentifier = "we used our other secret key"; MacaroonsBuilder mb = new MacaroonsBuilder(location, secret, publicIdentifier)
  .add_first_party_caveat("account = 3735928559");
  // add a 3rd party caveat // you'll likely want to use a higher entropy source to generate this key String caveat_key = "4; guaranteed random by a fair toss of the dice"; String predicate = "user = Alice"; // send_to_3rd_party_location_and_do_auth(caveat_key, predicate);
 // identifier = recv_from_auth();
 String identifier = "this was how we remind auth of key/pred"; Macaroon m = mb.add_third_party_caveat("http://auth.mybank/", caveat_key, identifier)
  .getMacaroon();

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

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:

Macaroon d = new MacaroonsBuilder("http://auth.mybank/", caveat_key, identifier)
  .add_first_party_caveat("time < 2015-01-01T00:00")
  .getMacaroon();

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:

Macaroon dp = MacaroonsBuilder.modify(m)
  .prepare_for_request(d)
  .getMacaroon();

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 = 82a80681f9f32d419af12f6a71787a1bac3ab199df934ed950ddf20c25ac8c65 // > dp.signature = 2eb01d0dd2b4475330739140188648cf25dda0425ea9f661f1574ca0a9eac54e

The root macaroon 'm' and its discharge macaroons 'dp' 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:

new MacaroonsVerifier(m)
  .satisfyExact("account = 3735928559")
  .satisfyGeneral(new TimestampCaveatVerifier())
  .satisfy3rdParty(dp)
  .assertIsValid(secret);
 // > ok.

Without the 'prepare_for_request()' call, the verification would fail.

Commonly used verifier, shipped with jmacaroons

Time to live verification

Applying a timestamp in the future to a macaroon will provide time to live semantics. Given that all machines have synchronized clocks, a general macaroon verifier is able to check for expiration.

Macaroon macaroon = new MacaroonsBuilder(location, secretKey, identifier)
  .add_first_party_caveat("time < 2015-01-01T00:00")
  .getMacaroon();
  new MacaroonsVerifier(macaroon)
  .satisfyGeneral(new TimestampCaveatVerifier())
  .isValid(secretKey);
 // > True
Authorities verification

Macaroons may also embed authorities. Thus a general macaroon verifier is able to check for a single authority.

// import static com.github.nitram509.jmacaroons.verifier.AuthoritiesCaveatVerifier.hasAuthority; Macaroon macaroon = new MacaroonsBuilder(location, secretKey, identifier)
  .add_first_party_caveat("authorities = ROLE_USER, DEV_TOOLS_AVAILABLE")
  .getMacaroon();
  new MacaroonsVerifier(macaroon)
  .satisfyGeneral(hasAuthority("DEV_TOOLS_AVAILABLE"))
  .isValid(secretKey);
 // > True

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 jmacaroons library exposes a constant that is the ideal number of bytes these secret keys should contain. Any shorter is wasting an opportunity for security.

com.github.nitram509.jmacaroons.MacaroonsConstants.MACAROON_SUGGESTED_SECRET_LENGTH = 32

Performance

There's a little micro benchmark, which demonstrates the performance of jmacaroons.

Source: https://gist.github.com/nitram509/b6f836a697b405e5f440

Environment: Windows 8.1 64bit, JRE 1.8.0_25 64bit, Intel i7-4790 @3.60GHz

Results ---------- Benchmark

  Mode  Samples

  Score

 Error  Units o.s.JMacaroonsBenchmark.benchmark_Deserialize

 thrpt

  5  2190474,677 ± 44591,197  ops/s o.s.JMacaroonsBenchmark.benchmark_Deserialize_and_Verify_key_bytes

 thrpt

  5
457262,262 ±  5868,723  ops/s o.s.JMacaroonsBenchmark.benchmark_Deserialize_and_Verify_key_string

thrpt

  5
262689,398 ±  4270,857  ops/s o.s.JMacaroonsBenchmark.benchmark_Serialize_with_key_bytes

thrpt

  5
424008,024 ± 16222,450  ops/s o.s.JMacaroonsBenchmark.benchmark_Serialize_with_key_bytes_and_1_caveat
  thrpt

  5
242060,835 ±  5696,272  ops/s o.s.JMacaroonsBenchmark.benchmark_Serialize_with_key_bytes_and_2_caveats
 thrpt

  5
166017,277 ±
870,467  ops/s o.s.JMacaroonsBenchmark.benchmark_Serialize_with_key_bytes_and_3_caveats
 thrpt

  5
127712,773 ±
478,394  ops/s o.s.JMacaroonsBenchmark.benchmark_Serialize_with_key_string

  thrpt

  5
252302,839 ±  3277,232  ops/s 

Resources

Context free and basic utils to build Android project conveniently.

This library offers some barebone code for android common to most applications. It provides simple classes and pre-written functions for:

  • Internet Access
  • SharedPreferences storage and retrieval
  • ImagePicker and Bitmap operations
  • File read and write
  • Recycler View
  • Image Downloading
  • Database support
  • Json Parsing

Thrifty is an implementation of the Apache Thrift software stack for Android.

Thrift is a widely-used cross-language service-definition software stack, with a nifty interface definition language from which to generate types and RPC implementations. Unfortunately for Android devs, the canonical implementation generates very verbose and method-heavy Java code, in a manner that is not very Proguard-friendly.

This is a simple EditText which fades at the end when text goes beyond the screen.

An RxJava transformer which combines replay, publish, and refCount operators.

An implement of RecyclerView with HeaderViews.

Topics


2D Engines   3D Engines   9-Patch   Action Bars   Activities   ADB   Advertisements   Analytics   Animations   ANR   AOP   API   APK   APT   Architecture   Audio   Autocomplete   Background Processing   Backward Compatibility   Badges   Bar Codes   Benchmarking   Bitmaps   Bluetooth   Blur Effects   Bread Crumbs   BRMS   Browser Extensions   Build Systems   Bundles   Buttons   Caching   Camera   Canvas   Cards   Carousels   Changelog   Checkboxes   Cloud Storages   Color Analysis   Color Pickers   Colors   Comet/Push   Compass Sensors   Conferences   Content Providers   Continuous Integration   Crash Reports   Credit Cards   Credits   CSV   Curl/Flip   Data Binding   Data Generators   Data Structures   Database   Database Browsers   Date &   Debugging   Decompilers   Deep Links   Dependency Injections   Design   Design Patterns   Dex   Dialogs   Distributed Computing   Distribution Platforms   Download Managers   Drawables   Emoji   Emulators   EPUB   Equalizers &   Event Buses   Exception Handling   Face Recognition   Feedback &   File System   File/Directory   Fingerprint   Floating Action   Fonts   Forms   Fragments   FRP   FSM   Functional Programming   Gamepads   Games   Geocaching   Gestures   GIF   Glow Pad   Gradle Plugins   Graphics   Grid Views   Highlighting   HTML   HTTP Mocking   Icons   IDE   IDE Plugins   Image Croppers   Image Loaders   Image Pickers   Image Processing   Image Views   Instrumentation   Intents   Job Schedulers   JSON   Keyboard   Kotlin   Layouts   Library Demos   List View   List Views   Localization   Location   Lock Patterns   Logcat   Logging   Mails   Maps   Markdown   Mathematics   Maven Plugins   MBaaS   Media   Menus   Messaging   MIME   Mobile Web   Native Image   Navigation   NDK   Networking   NFC   NoSQL   Number Pickers   OAuth   Object Mocking   OCR Engines   OpenGL   ORM   Other Pickers   Parallax List   Parcelables   Particle Systems   Password Inputs   PDF   Permissions   Physics Engines   Platforms   Plugin Frameworks   Preferences   Progress Indicators   ProGuard   Properties   Protocol Buffer   Pull To   Purchases   Push/Pull   QR Codes   Quick Return   Radio Buttons   Range Bars   Ratings   Recycler Views   Resources   REST   Ripple Effects   RSS   Screenshots   Scripting   Scroll Views   SDK   Search Inputs   Security   Sensors   Services   Showcase Views   Signatures   Sliding Panels   Snackbars   SOAP   Social Networks   Spannable   Spinners   Splash Screens   SSH   Static Analysis   Status Bars   Styling   SVG   System   Tags   Task Managers   TDD &   Template Engines   Testing   Testing Tools   Text Formatting   Text Views   Text Watchers   Text-to   Toasts   Toolkits For   Tools   Tooltips   Trainings   TV   Twitter   Updaters   USB   User Stories   Utils   Validation   Video   View Adapters   View Pagers   Views   Watch Face   Wearable Data   Wearables   Weather   Web Tools   Web Views   WebRTC   WebSockets   Wheel Widgets   Wi-Fi   Widgets   Windows   Wizards   XML   XMPP   YAML   ZIP Codes