multi-authority attribute based encryption
Mosaic is a library doing Attribute Based Encryption (ABE). It is meant to be used as a example of a cryptographic core to be embedded in any solution willing to rely on an ABE scheme of this kind
(a Multi-Authority CipherText-Policy scheme, see below).
The code is for demonstration purposes only and it is not suitable
for embedding in any reliable cryptographic solutions.
A quantity of structure which should be needed in any reliable cryptographic solution is missing in the code base
(about randomness, hashing functions used, selection of curves and pairings and underlying cryptographic library, hash-to-groups primitives,
secure communication, in-memory management of cryptographic calculations and many other issues).
Moreover, the code is not fully tested (nor formally verified),
nor yet implementing any reliable error management procedure and
should not be used in production environments.
The library is under the copyleft license AGPL-3.0.
library is under development (APIs are subject to change) please check for updates
Attribute based encryption is about sharing a secret according to attributes and policies.
A policy is an expression (boolean or integer-valued with linear constraints)
whose variables are called attributes.
The cryptographic scheme allows the encryption of a secret
according to a given policy, splitting the secret among the attributes mentioned in the policy.
The secret could be recovered only by a set of keys corresponding to attributes
which satisfy the policy (i.e., make the policy expression evaluate to true).
Those keys are attached to attributes and users (both strings or, in general, hashable objects).
Users can obtain these attribute-keys asking some authority for them
(so, the secret could only be recovered by those users having enough attributes as to satisfy the policy).
In a sense, attribute based encryption generalizes asymmetric cryptography:
the communication is no more 1:1 and the sender (encrypting) could ignore
the users allowed to receive (decrypt) her messages.
Assume a company wants to manage the access to certain documents among its employees.
So, the company instantiate an authority company, so that users can ask this authority
for attributes like logistics@company or it@company or topsecret@company.
It is up to the authority to decide whether to give a given attribute-key to a user or not.
A user that needs to encrypt some documents can create a policy like
logistics@company \/ topsecret@company (meaning that the policy is meant to grant
access rights to those user having keys for logistics@company or for topsecret@company),
and use this policy to encrypt the documents.
Now, the authorization layer to the documents is embedded in the mathematics,
meaning that only those users having the appropriate keys will be able
to decrypt the documents.
Policy can also include linear constraints on non-negative integer-valued attributes,
so, users can ask for attributes like level@company with some value from 0 onwards,
to be used in a policy like topsecret@company \/ (level@company > 0 )
The code implements the Multi-Authority CipherText-Policy scheme as described in
Efficient Statically-Secure Large-Universe Multi-Authority
Attribute-Based Encryption by Yannis Rouselakis and Brent Waters.
Specifically, the implementation
Syntax for attributes and policies:
To use the library in a Go environment
import "github.com/marcellop71/mosaic/abe"
Core Go APIs are wrapped by JSON APIs so that every function
has only string type parameters as inputs and outputs
(string could be a plain or a JSON string depending on the function).
Everything binary is encoded to base32
(both JSON strings and field values in JSON objects).
Given the JSON APIs, the library could also be exported as a shared object (resulting in libmosaic.h and libabe.so in ./lib usable by many languages). The wrapping is trivial because there are only char* in each signature.
go build -tags=z3,miracl -buildmode c-shared -o lib/libmosaic.so lib/mosaic.go
(or lib/mosaic_pbc.go)
GO API | GO API (JSON) | C API |
---|---|---|
NewRandomOrg (lib string, curve Curve) Org | NewRandomOrgJson(string, string) string | newRandomOrg |
NewRandomAuth(org Org) AuthKeys | NewRandomAuthJson(string) string | newRandomAuth |
NewRandomUserkey(user string, attr string, authprv AuthPrv) Userkey | NewRandomUserkeyJson(string, string, string) string | newRandomUserkey |
NewRandomSecret(org Org, seed string) Point | NewRandomSecretJson(string, string) string | newRandomSecret |
AuthPubsOfPolicy(policy string) AuthPubs | AuthPubsOfPolicyJson(string) string | authpubsOfPolicy |
PolicyOfCiphertext(ct Ciphertext) string | PolicyOfCiphertextJson(string) string | policyOfCiphertextJson |
SelectUserAttrs(user string, policy string, userattrs Userattrs) Userattrs | SelectUserAttrsJson(string, string, string) string | selectUserAttrs |
Encrypt(secret Point, policy string, authpubs AuthPubs) string | EncryptJson(string, string, string) string | encrypt |
Decrypt(ct Ciphertext, userattrs Userattrs) Point | DecryptJson(string, string) string | decrypt |
RewritePolicy(policy string) string | RewritePolicy(string) string | rewritePolicy |
CheckPolicy(policy string, userattrs Userattrs) string | CheckPolicyJson(string, string) string | checkPolicy |
GetPbcCurve(curve string) Curve | GetPbcCurve(string) string | getPbcCurve |
RewritePolicy and CheckPolicy needs z3 support, while GetPbcCurve needs pbc.
Please remark that the support by z3 and miracl (or pbc) is managed by the build tags “z3”, “miracl” and “pbc”. For example, run the example with
go run -tags=z3,miracl examples/ex_JsonAPI.go
Here, the signature is described by a suffix Str (plain string)
or Json (json string, for the objects serialized as json strings).
export | signature | json schema for output |
---|---|---|
newRandomOrg | libStr, CurveJson (empty if not pbc) -> orgJson | json to be used as-is |
newRandomAuth | orgJson -> authkeysJson | { "authpub": "authority public key json string", "authprv": "authority private key json string"} |
newRandomUserkey | userStr, attrStr, authprvJson -> userattrsJson | {"user": "user string", "coeff": {"attribute0": [], ...}, "userkey": {"attribute0": "userkey json string", ...}} |
newRandomSecret | orgJson, seedStr -> secretStr | plain string |
rewritePolicy | policyStr -> policyStr | plain string |
checkPolicy | policyStr, userattrsJson (possibly empty) -> string {“sat”, “unsat”} | plain string |
extractAuthsFromPolicy | policyStr -> authpubsJson | {"authpub": {"authority0": "authority public key", "authority1": "authority public key json string", ...}} |
extractPolicyFromCiphertext | ciphertextJson -> policyStr | plain string |
selectUserAttrs | userStr, policyStr, userattrsJson -> userattrsJson | {"user": "user string", "coeff": {"attribute0": [], ...}, "userkey": {"attribute0": "userkey json string", ...}} |
encrypt | secretJson, policyStr, authpubsJson -> ciphertextJson | json to be used as-is |
decrypt | ciphertextJson, userattrsJson -> secretStr | plain string |
getPbcCurve (only for pbc) | curveStr -> CurveJson | json to be used as-is |
The json strings returned by getCurve, newRandomOrg and encrypt are meant to be used as-is: stored somewhere or re-fed into the relevant functions.
An application needs to interact only with authkeysJson, authpubsJson and userattrsJson.
authkeysJson holds the public and private keys for a new authority.
authpubsJson is a json holding in the key ‘authpub’ the association between an authority (name)
and its corresponding public key, while userattrsJson holds the associations
between an attribute and its use (coefficent) in the key “coeff” and
between an attribute and its corresponding user key in the key “key”.
type Org struct { ... } // organization
type AuthPub struct { ... } // authority public params
type AuthPrv struct { ... } // authority private params
type Userkey struct { ... } // user key
type Ciphertext struct { ... } // ciphertext encrypting the msg
type AuthKeys struct { // authority public and private keys
AuthPub string `json:"authpub"` // type AuthPub
AuthPrv string `json:"authprv"` // type AuthPrv
}
type AuthPubs struct { // map of authorities public keys
AuthPub map[string]string `json:"authpub"` // type AuthPub
}
type UserAttrs struct { // map of user attributes
User string `json:"user"` // user
Coeff map[string][]int `json:"coeff"` // attr -> its coefficients
Userkey map[string]string `json:"userkey"` // Userkey
}
The only external dependency that needs to be available is z3 (optionally, also pbc).
Internally, the Go packages used are: logrus for logging and antlr4 for parsing.
The examples (not the library, see below) use
Dependencies or database support could be easily installed:
You can build a docker images with everything:
./docker/build_image.sh
Source is made up of 2 Go packages:
The file structure of the core lib is the following:
file | description |
---|---|
arith_miracl.go | miracl interface |
arith_pbc.go | pbc interface |
crypto.go | ABE scheme algorithms and JSON APIs |
policy_z3.go | policy management using z3 |
policy.go | parsing and policy management |
sss.go | linear secret sharing scheme |
types.go | structs and their serialization |
Policy.g4 | antlr4 parser grammar |
PolicyLexer.g4 | antlr4 lexer grammar |
log/ | logging |
miracl/ | miracl core library (Go version) |
parser/ | policy parser (generated by antlr4 using the ./abe/policy.g4 grammar file) |
pbc/ | pbc library wrapper |
z3/ | z3 library wrapper |
An ./examples/ex_GoAPI_noservice_noz3.go is there to show how the APIs could be used.
The aim is to encrypt a secret according to a given policy (iterating
over a small set of policies) and recover it (if feasible),
according to a selection of attributes given to a user.
go run -tags=z3,miracl examples/ex_GoAPI_noservice_noz3.go
or
docker run -it mosaic:latest go run -tags=z3,miracl examples/ex_GoAPI_noservice_noz3.go
./examples/ex_GoAPI_noservice_noz3.go is an application using the github.com/mosaic/abe package
and a ./mosaic/service package to handle some storage for the keys,
the attributes and the users.
The service package implemented can use Leveldb
or a Redis server.
Details in the configuration file ./examples/config.yaml
config:
arithmetic:
library: miracl
curve: BN254
seed: abcdef
storage:
redis:
local0:
addr: "127.0.0.1:6379"
password: ""
leveldb:
local0:
name: "mosaic.db"
active:
type: "leveldb"
label: "local0"
The workflow implemented in the example is basic.
Using attribute based encryption for a document sharing application
could mean that an encryptor:
now if a user needs to access the document:
service.SetupOrg("org0", "miracl", "BN254", seed) // setting up an organization (hosting a set of authorities) onto a given curve
service.SetupAuth("auth0", "org0") // setting up an authority into a given organization
policy := "A@auth0 /\\ (B@auth0 /\\ (C@auth0 \\/ D@auth0))" // a policy as a standard boolean expression
// encrypting
secret := service.NewRandomSecret("org0") // create a new secret
policy = abe.RewritePolicy(policy) // simplify policy
auths := abe.AuthPubsOfPolicyJson(policy) // extracts authorities mentioned in the policy
auths = service.FetchAuthPubs(auths) // collects public keys of those authorities
secret_enc := abe.EncryptJson(secret, policy, auths) // encrypts the secret into a ciphertext
// user asking for keys
user := "marcello.paris@gmail.com"
service.SetupUserkey(user, "A@auth0") // for the user creates a key corresponding to the given attribute
service.SetupUserkey(user, "B@auth0") // for the user creates a key corresponding to the given attribute
service.SetupUserkey(user, "C@auth0") // for the user creates a key corresponding to the given attribute
service.SetupUserkey(user, "D@auth0") // for the user creates a key corresponding to the given attribute
// decrypting
policy = abe.PolicyOfCiphertextJson(secret_enc) // extract the policy embedded in the ciphertext
userattrs := service.FetchUserAttrs(user) // collects the available user attributes
userattrs = abe.SelectUserAttrsJson(user, policy, userattrs) // select which user attributes (if any) are useful for the given policy
userattrs = service.FetchUserkeys(userattrs) // collects user keys corresponding to the useful attributes
secret_dec := abe.DecryptJson(secret_enc, userattrs) // decrypts the ciphertext into the secret plaintext
policy := "(A@auth0 > 1) /\\ B@auth0"
...
service.SetupUserkey(user, "A=5@auth0")
Use “BN254” or “BLS12381” or “BN462” if using miracl (recommended)
or use “SS512” or “BN254” if using pbc.
Let the pairing be G1 x G2 -> GT
The scheme encrypts a point in GT as: