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
- is based on elliptic curves pairings, powered by miracl (recommended) (code included in this repo) or pbc (including parts of the Go wrapper https://github.com/Nik-U/pbc) [rf. to config file later in this doc]
- uses linear secret sharing schemes as explained in section G of Decentralizing Attribute-Based Encryption by Allison Lewko and Brent Waters
- allows numerical expressions in policies (see paragraph below) using a bag-of-bits representation of attributes as in Ciphertext-Policy Attribute-Based Encryption by John Bethencourt, Amit Sahai, Brent Waters
- uses z3 to verify, simplify and rewrite policies (it could use z3 also to solve and get hints from policies)
- collects the authorities into organizations: policy are allowed to mention only attributes from authorities within a given organization
- uses an elementary hash-to-group function to map attribute and user strings to the group
Syntax for attributes and policies:
- boolean attributes are in the form attribute@authority (cfr. notation already used in Charm)
- integer-valued attributes are written attribute=value@authority
- policies are arbitrary expressions in attributes with operators /\ (AND) and \/ (OR)
- policies allow == < <= > >= between an attribute (on the left) and a numerical value (on the right)
- supporting also S-expressions for 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.
以下代码跑不了,其中的abe.NewRandomSecretJson()函数,have(string, sting),而want(string)。
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
or
docker run -it mosaic:latest 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
- Leveldb (via the Go wrapper https://github.com/jmhodges/levigo)
- or a Redis server (via the Go wrapper https://github.com/go-redis/redis)
Dependencies or database support could be easily installed:
- mac os: brew install z3 and brew install leveldb redis (in case also, brew install pbc)
- linux: see ./docker/Dockerfile
You can build a docker images with everything:
./docker/build_image.sh
Source is made up of 2 Go packages:
- abe: core package defining basic types and the cryptographic scheme
- service: service package defining interactions with databases
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
service 是一个key-value存储包装类。 ./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:
- creates a random secret
- uses this secret to encrypt (symmetric) a document
- creates a policy string describing access to the document
- creates an encrypted version secret_enc of the secret according to the policy
- shares the encrypted document and the secret_enc + an hashed version of the secret secret_hash
now if a user needs to access the document:
- user asks an authority for its keys corresponding to a given attribute
- user decrypts the secret_enc into a clear secret
- user checks if she can recover the secret_hash
- user uses this secret to decrypt 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:
- a point on GT (the point to be encrypted perturbed by a secret)
- a bags of 4-points tuples (i.e., bag of points on GT x G1 x G1 x G2) [one tuple for each attribute-leave in the policy]
在Codespace下运行项目,执行如下的install.sh
脚本,直接使用go run
命令运行examples
目录下的文件。
./install.sh
go run -tags=z3,miracl examples/ex_JsonAPI.go
go run -tags=z3,miracl examples/ex_JsonAPI_linear_constraints.go
当项目中无go.mod时
go mod init github.com/marcellop71/mosaic
go get github.com/antlr/antlr4/runtime/Go/antlr@4.7.2
go mod tidy
安装LevelDB
export VER="1.20"
wget https://github.com/google/leveldb/archive/v${VER}.tar.gz
tar xvf v${VER}.tar.gz
rm -f v${VER}.tar.gz
cd leveldb-${VER}
make
sudo scp -r out-static/lib* out-shared/lib* "/usr/local/lib"
cd include
sudo scp -r leveldb /usr/local/include
sudo ldconfig
安装z3解释器
pip install z3-solver
z3 --version
ECC:(Elliptic Curve Cryptography)椭圆曲线密码学 SMT:可满足性模理论(satisfiability modulo theories, SMT)
MIRACL Core 是一个多语言且与架构无关的密码库,支持椭圆曲线加密、配对友好曲线加密、RSA、AES 对称加密和哈希函数。 支持曲线SM2, BN462, BLS48581, C13318, JUBJUB, X448, SECP160R1, C1174, BLS48286, TWEEDLEDUM, TWEEDLEDEE
z3是由微软公司开发的一个优秀的SMT求解器(也就定理证明器),它能够检查逻辑表达式的可满足性。
Antlr4 是一款强大的语法生成器工具,可用于读取、处理、执行和翻译结构化的文本或二进制文件。