Skip to content

Commit

Permalink
initial import
Browse files Browse the repository at this point in the history
  • Loading branch information
Geal committed Dec 17, 2018
0 parents commit 59a0743
Showing 1 changed file with 197 additions and 0 deletions.
197 changes: 197 additions & 0 deletions README.md
@@ -0,0 +1,197 @@
# Biscuit Authentication

## Introduction

## Inspiration

This system draws ideas from X509 certificates,
JWT, macaroons and vanadium.

# Goals and prior art

distributed authorization is traditionally done through
centralized systems like OAuth, where any new authorization
will be delivered by a server, and validated by that same server.
This is fine when working with a monolithic system, or a small
set of microservices.
A request coming from a user agent could result in hundreds of
internal requests between microservices, each requiring a verification
of authorization.

JSON Web Tokens were designed in part to handle distributed authorization,
and in part to provide a stateless authentication token.
While it has been shown that state management cannot be avoid (it is
the only way to have correct revocation), distributed authorization
has proven useful. JSON Web Tokens are JSON objects that carry
data about their principal, expiration dates and a serie of claims,
all signed by the authorization server's public key. Any service that
knows and trusts that public key will be able to validate the token.
JWTs are also quite large and often cannot fit in a cookie, so they are
often stored in localstorage, where they are easily stolen via XSS.

Macaroons provide a token that can be delegated: the holder can
create a new, valid token from the first one, by attenuating its
rights. They are built from a secret known to the authorization server.
A token can be created from a caveat and the HMAC of the secret and the caveat.
To build a new token, we add a caveat, remove the previous HMAC signature,
and add a HMAC of the previous signature and the new caveat (so from
an attenuated token we cannot go back to a more general one).
This allows use to build tokens with very limited access, that wan can hand
over to an external service, or build unique restricted tokens per requests.
Building macaroons on a secret means that any service that wants to validate
the token must know that secret.

Vanadium builds a distributed authorization and delegation system
based on public keys, by binding a token to a public key with
a certificate, and a blessing (a name with an optional prefix).
Attenuating the token means generating a new blessing by appending
a name, and signing a list of caveats and the public key of the new
holder. The token is then validated first by validating the certificate,
then validating the caveats, then applying ACLs based on patterns
in the blessings.


Here is what we want:
- distributed authorization: any node could validate the token only with public information
- delegation: a new, valid token can be created from another one by attenuating its rights
- avoiding identity and impersonation: in a distributed system, not all services
need to know about the token holder's identity. Instead, they care about
specific authorizations
- capabilities: a request carries a token that contains a set of rights
that will be used for authorization, instead of deploying ACLs on every node

## Format

A biscuit token is an ordered list of key and value tuples, stored in HPACK
format. HPACK was chosen to avoid specifying yet another serialization format,
and reusing its data compression features to make tokens small enough to
fit in a cookie.

biscuit := block\*, signature
block := HPACK{ kv\* }
kv := ["rights", rights] | ["pub", pubkey] | [TEXT, TEXT]
TEXT := characters (UTF-8 or ASCII?)
pubkey := base64(public key)
rights := namespace { right,\* }
namespace := TEXT
right := (+|-) tag : feature(options)
tag := TEXT | /regexp/ | *
feature := TEXT | /regexp/ | *
options := (r|w|e),\*

Example:

[
issuer = Clever Cloud
user = user_id_123
rights = clevercloud{-/.*prod/ : *(*) +/org_456-*/: *(*) +lapin-prod:log(r) +sozu-prod:metric(r)}
]
<signature = base_64(64 bytes signature)>

This token was issued by "Clever Cloud" for user "user_id_123".
It defines the following capabilities, applied in order:
- remove all rights from any tag with the "prod" suffix
- give all rights on any tag that has the "org_456" prefix (even those with "prod" suffix)
- add on the "lapin-prod" tag the "log" feature with right "r"
- add on the "sozu-prod" tag the "metric" feature with right "r"

Example of attenuated token:

[
issuer = Clever Cloud
user = user_id_123
organization = org_456
rights = clevercloud{-/.*prod/ : *(*) +/org_456-*/: *(*) +lapin-prod:log(r) +sozu-prod:metric(r)}
]
[
pub = base64(128 bytes key)
rights = clevercloud { -/org_456-*/: *(*) +/org_456-test/ database(*) }
]
<signature = base_64(new 64 bytes signature)>

This new token starts from the same rights as the previous one, but attenuates it
that way:
- all access to tags with "org_456-" prefix is removed
- except that "org_456-test" tag, on which we activate the "database" feature with all accesses

The new token has a signature derived from the previous one and the second block.

## Common keys and values

Key-value tuples can contain arbitrary data, but some of them have predefined
semantics (and could be part of HPACK's static tables to reduce the size of
the token):
- issuer: original creator of the token (validators will be able
## Cryptography

ISSUER = $issuer_token$
// think about repudation
USER = $user_id$ // using f for field
rights = +/-(tagregexp):feature_name(options),feature_name(options,options) -tag:feature(w)
rights =

log(r)
log_drain(r,w)
apps(r,w)
metric(r)
domain

/.+/ = *

+/.+/:/.+/(/.+/) -/.*prod/:/.+/(/.+/)
+*:*(*)

clevercloud{ }
//
alias * = /.+/



*{-*:*(*)}
clevercloud{+sozu:metric(r) +/.*rust/:/.+/(/.+/) -/.*prod/:/.+/(/.+/) +lapin:log(r)}

# Pairing based cryptography

https://en.wikipedia.org/wiki/Pairing-based_cryptography
Pairing e: G1 x G2 -> Gt with G1 and G2 two additive cyclic groups of prime order q, Gt a multiplicative cyclic group of order q
with a, b from Fq* finite field of order q
with P from G1, Q from G2
e(aP, bQ) == e(P, Q)^(ab)
e != 1

# more specifically:
e(aP, Q) == e(P, aQ) == e(P,Q)^a
e(P1 + P2, Q) == e(P1, Q) * e(P2, Q)

# Signature
choose k from Fq* as private key, g2 a generator of G2
public key P = k*g2

Signature S = k*H1(message) with H1 function to hash message to G1
Verifying: knowing message, P and S
e(S, g2) == e( k*H1(message), g2)
== e( H1(message), k*g2)
== e( H1(message), P)

# Signature aggregation
knowing messages m1 and m2, public keys P1 and P2
signatures S1 = Sign(k1, m1), S2 = Sign(k2, m2)
the aggregated signature S = S1 + S2

Verifying:
e(S, g2) == e(S1+S2, g2)
== e(S1, g2)*e(S2, g2)
== e(k1*H1(m1), g2) * e(k2*HA(m2), g2)
== e(H1(m1), k1*g2) * e(H1(m2), k2*g2)
== e(H1(m1), P1) * e(H1(m2), P2)
so we calculate signature verification pairing for every caveat
then we multiply the result and check equality

we use curve BLS12-381 (Boneh Lynn Shacham) for security reasons
(cf https://github.com/zcash/zcash/issues/2502
for comparions with Barreto Naehrig curves)
assumes computational Diffe Hellman is hard

Example of library this can be implemented with:
- pairing crate: https://github.com/zkcrypto/pairing
- mcl: https://github.com/herumi/mcl

0 comments on commit 59a0743

Please sign in to comment.