Welcome to the Zenroom documentation wiki!
Getting started
Build instructions
This section is optional for those who want to build this software from source. The following build instructions contain generic information meant for an expert audience.
The Zenroom compiles the same sourcecode to run on Linux in the form of 2 different POSIX compatible ELF binary formats using GCC (linking shared libraries) or musl-libc (fully static) targeting both X86 and ARM architectures. It also compiles to a Windows 64-bit native and fully static executable. At last, it compiles to Javascript/Webassembly using the LLVM based emscripten SDK. To recapitulate some Makefile targets:
make shared
its the simpliest, builds a shared executable linked to a system-wide libc, libm and libpthread (mostly for debugging)make static
builds a fully static executable linked to musl-libc (to be operated on embedded platforms)make javascript-node
,make javascript-wasm
(need EMSDK env) builds different flavors of Javascript modules to be operated from a browser or NodeJS (for client side operations)make win
builds a Windows 64bit executable with no DLL dependancy, containing the LUA interpreter and all crypto functions (for client side operations on windows desktops)make javascript-demo
creates in thedocs/demo
folder a simple web page with a REPL and some boilerplate code to show how to use the WebAssembly binary (visible online here)
Remember that if after cloning this source code from git, one should do:
git submodule update --init --recursive
Then first build the shared executable environment:
make shared
To run tests:
make check-shared
To build the static environment:
make bootstrap
make static
make check-static
For the Javascript and WebAssembly modules the Zenroom provides various targets provided by emscripten which must be installed and loaded in the environment according to the emsdk’s instructions :
make javascript-node
make javascript-wasm
make javascript-demo
NB. for the javascript-demo
target the generated files should be served by a http server and not directly opened in a browser eg.
$ make javascript-demo
$ cd docs/demo
$ python3 -m http.server
Build instructions for Mobile libraries
iOS
You need to have install Xcode
with the commandline-tools
There are 3 different targets ios-sim
ios-armv7
ios-arm64
these targets creates an static library with the correct architecture (x86_64, ARMV7, ARM64).
Finally once done all the libraries there is a final target ios-fat
that put them together creating a fat-binary that you can include into your app.
Or you can just use the build-ios.sh
that does all the steps for you!
For using the library just copy zenroom.a
somewhere in your project and include the zenroom.h file.
Android
You need to have installed android-sdk
(if you have Android Studio installed is already there) and set the ANDROID_HOME
variable.
Also you need to install NDK inside the android-sdk using the Android Studio -> Tools -> Android -> SDK Manager
Finally use the builld-android.sh
script (be sure that the ANDROID_HOME environment var is set) and you will have at the end libzenroom-arm.so
and libzenroom-x86.so
To use it in your project just drop src/Zenroom.java
inside your codebase and the put the *.so
as following:
src/
main/
java/
jniLibs/
x86/
libzenroom.so
armeabi/
libzenroom.so
Embedding Zenroom
Zenroom is designed to facilitate embedding into other native applications and high-level scripting languages. The stable releases distribute compiled library components for Apple/iOS and Google/Android platforms, as well MS/Windows DLL. Golang bindings and a Jupyter kernel are also in experimental phase.
To call Zenroom from an host program is very simple, since there isn’t an API of calls, but a single call to execute scripts and return their results. The call is called zenroom_exec
and prints results to the "stderr/stdout" terminal output. Its prototype is common to all libraries:
int zencode_exec(char *script, char *conf, char *keys, char *data);
in case buffers should be used instead of stdout/err file descriptors, this call defines where to print out the output and the maximum sizes allowed for it. Output is NULL terminated. The input buffers are all read-only, here their functions:
script
: a long string containing the script to be executedconf
: a short configuration string (for now onlyumm
supported as value)keys
: a string often JSON formatted that contains keys (sensitive information)data
: a string (also JSON formatted) that contains data
In addition to this function there is another one that copies results (error messages and printed output) inside memory buffers:
int zenroom_exec_tobuf(char *script, char *conf, char *keys, char *data,
char *stdout_buf, size_t stdout_len,
char *stderr_buf, size_t stderr_len);
In addition to the previously explained arguments, the new ones are:
stdout_buf
: pre-allocated buffer by the caller where to copy stdoutstdout_len
: maximum length of the pre-allocated stdout bufferstderr_buf
: pre-allocated buffer by the called where to copy stderrstderr_len
: maximum length of the pre-allocated stderr buffer
Language bindings
-This API can be called in similar ways from a variety of languages and wrappers that already facilitate its usage.
Javascript
? Installation
npm install zenroom
or
yarn add zenroom
? Quick Usage
const zenroom = require('zenroom').default
const script = `print("Hello World!")`
zenroom.script(script).zenroom_exec()
// prints in the console.log "Hello World!"
Python
? Installation
pip install zenroom
? Quick Usage
from zenroom import zenroom
script = "print('Hello world!')"
result = zenroom.zenroom_exec(script)
print(result.stdout) # guess what
Golang
? Installation
import "github.com/DECODEproject/Zenroom/bindings/golang/zenroom"
? Quick Usage
script := []byte(`print("Hello World!")`)
res, _ := zenroom.Exec(script)
fmt.Println(string(res))
How to execute Zenroom scripts
This section explains how to invoke the Zenroom to execute scripts in various situations: from commandline, from javascript and various other languages.
Javascript has its own way to call zenroom_exec(...)
without losing all the context for execution.
Other languages are recommended to exec()
the Zenroom in a separate process and gather its stdout
and its return value (1 on error and 0 on success).
Commandline
From command-line the Zenroom is operated passing files as
arguments:
Usage: zenroom [-dh] [-c config] [-k KEYS] [-a DATA] [ SCRIPT.lua | - ]
The -d
flag activates more verbose output for debugging.
The SCRIPT.lua
can be the path to a script or a single dash (-
) to instruct zenroom to process the script piped from stdin
.
Interactive console
Just executing zenroom
will open an interactive console with limited functionalities, which is capable to parse finite instruction blocks on each line. To facilitate editing of lines is possible to prefix it with readline using the rlwrap zenroom
command instead.
The content of the KEYS, DATA and SCRIPT files cannot exceed 500KB.
Try:
./zenroom-static [enter]
print("Hello World!") [enter]
[Ctrl-D] to quit
Will print Zenroom’s execution details on stderr
and the "Hello World!" string on stdout
.
Hashbang executable script
Zenroom also supports being used as an "hashbang" interpreter at the beginning of executable scripts. Just use:
#!/usr/bin/env zenroom-static -
in the header, please note the final dash which is pretty important to tell zenroom to process the script from stdin
.
Javascript
For javascript :tada: now a npm module is available on https://www.npmjs.com/package/zenroom
From javascript the function zenroom_exec()
is exposed with four arguments: four strings and one number from 1 to 3 indicating the verbosity of output on the console.
This is the prototype of the javascript function:
const zenroom = (script_file, conf_file=null, keys_file=null,
data_file=null, verbosity=3)
Only the script_file
argument is mandatory, other arguments can be omitted, then defaults are used. For example after a make js
build we can invoke the zenroom in src/zenroom.js
creating such a script:
const fs = require('fs')
const zenroom_module = require(process.argv[2])
const zenroom = (script_file, conf_file=null, keys_file=null,
data_file=null, verbosity=3) => {
const enc = { encoding: 'utf8' }
const script = fs.readFileSync(script_file, enc)
const conf = (conf_file) ? fs.readFileSync(conf_file, env) : null
const keys = (keys_file) ? fs.readFileSync(keys_file, env) : null
const data = (data_file) ? fs.readFileSync(data_file, env) : null
return zenroom_module.ccall('zenroom_exec', 'number',
['string', 'string', 'string', 'string', 'number'],
[script, conf, keys, data, verbosity])
}
console.log("[JS] zenroom_exec %s %s", process.argv[2], process.argv[3])
zenroom(process.argv[3])
If the above script is saved in zenroom_exec.js
then on the commandline it can be invoked with the path to zenroom.js
as first parameter and the path to a script as second parameter:
nodejs src/zenroom.js zenroom_exec.js examples/hello.lua
The contents of the three strings cannot exceed 500KB in size.
Data string
The data
is a simple string, but can be also a json map used to pass multiple arguments and complex data structures. Its contents can be accessed by the script
using the global variable DATA
(all uppercase).
For example create a json file containing a map (this can be a string
passed from javascript)
{
"secret": "zen and the art of programming",
"salt": "OU9Qxl3xfClMeiCz"
}
Then run zenroon -a arguments.json
and pass the following script as
final argument, or pipe from stdin or passed as a string argument to
zenroom_exec()
from javascript:
i = require "inspect"
json = require "json"
args = json.decode(DATA)
-- args is now a lua table containing values for each args.argname
i.print(args)
Keys string
The keys
is also a simple string passed from arguments, separated from DATA since its access permission from caller may be different due to specific privacy restrinctions that may be adopted on the variable. It is accessed by the script
using the global variable KEYS
.
So for instance if we want to encrypt a secret message for multiple
recipients who have provided us with their public keys, one would load
this example keyfile:
{
"keyring": {
"public":"GoTdVYbTWEoZ4KtCeMghV7UcpJyhvny1QjVPf8K4oi1i",
"secret":"9PSbkNgsbgPnX3hM19MHVMpp2mzvmHcXCcz6iV8r7RyZ"
},
"recipients": {
"jaromil": "A1g6CwFobiMEq6uj4kPxfouLw1Vxk4utZ2W5z17dnNkv",
"francesca": "CQ9DE4E5Ag2e71dUW2STYbwseLLnmY1F9pR85gLWkEC6",
"jimb": "FNUdjaPchQsxSjzSbPsMNVPA2v1XUhNPazStSRmVcTwu",
"mark": "9zxrLG7kwheF3FMa852r3bZ4NEmowYhxdTj3kVfoipPV",
"paulus": "2LbUdFSu9mkvtVE6GuvtJxiFnUWBWdYjK2Snq4VhnzpB",
"mayo": "5LrSGTwmnBFvm3MekxSxE9KWVENdSPtcmx3RZbktRiXc"
}
}
And then with this code encrypt a message to all recipients:
secret="this is a secret that noone knows"
-- this should be a random string every time
nonce="eishai7Queequot7pooc3eiC7Ohthoh1"
json = require "json"
crypto = require "crypto"
keys = json.decode(KEYS)
res = {}
for name,pubkey in pairs(keys.recipients) do
k = crypto.exchange_session_x25519(
crypto.decode_b58(keys.keyring.secret),
crypto.decode_b58(pubkey))
enc = crypto.encrypt_norx(k,nonce,secret)
-- insert in results
res[name]=crypto.encode_b58(enc)
end
print(json.encode(res))
The above script will print out the encrypted message for each recipient reorganised in a similar json structure:
{
"jaromil" : "Ha8185xZoiMiJhfquKRHvtT6vZPWifGaXmD4gxjyfHV9ASNaJF2Xq85NCmeyy4hWLGns4MTbPsRmZ2H7uJh9vEuWt",
"mark" : "13nhCBWKbPAYyhJXD7aeHtiFKb89fycBnoKy2nosJdSqfS2vhhHqBvVKb2oasiga9P3UyaEJZQdyYRfiBBKEswdmQ",
"francesca" : "7ro9u2ViXjp3AaLzvve4E4ebZNoBPLtxAja8wd8YNn51TD9LjMXNGsRvm85UQ2vmhdTeJuvcmBvz5WuFkdgh3kQxH",
"mayo" : "FAjQSYXZdZz3KRuw1MX4aLSjky6kbpRdXdAzhx1YeQxu3JiGDD7GUFK2rhbUfD3i5cEc3tU1RBpoK5NCoWbf2reZc",
"jimb" : "7gb5SLYieoFsP4jYfaPM46Vm4XUP2jbCUnkFRQfwNrnJfqaew5VpwqjbNbLJrqGsgJJ995bP2867nYLcn96wuMDMw",
"paulus" : "8SGBpRjZ21pgYZhXmy7uWGNEEN7wnHkrWtHEKeh7uCJgsDKtoGZHPk29itCV6oRxPbbiWEuN9Sm83jeZ1vinwQyXM"
}
Zencode
Smart contracts in human language
Zenroom is software inspired by the language-theoretical security research and it allows to express cryptographic operations in a readable domain-specific language called Zencode.
For the theoretical background see the Zencode Whitepaper.
For an introduction see this blog post: Smart contracts for the English speaker.
Here we go with the tutorial to learn the Zencode language!
Syntax and Memory model
Zencode contracts operate in 3 phases:
- Given – validates the input
- When – processes the contents
- Then – prints out the results
The 3 separate blocks of code also correspond to 3 separate memory areas, sealed by security measures.
If any single line in a Zencode contract fails, Zenroom stops executing and returns the error.
All data processed has first to pass the validation phase according to scenario specific data schemas.
Good Practice: start your Zencode noting down the Zenroom version you are using!
rule check version 1.0.0
Traditional cryptography in Zenroom
Symmetric encryption
This is a simple technique to hide a secret using a common password known to all participants.
The algorithm used is
AES-GCM with a random IV and an optional authenticated header (AEAD)
The encryption is applied using 3 arguments:
password
can be any string (or file) used to lock and unlock the secretmessage
can be any string (or file) to be encrypted and decryptedheader
is a fixed name and optional argument to indicate an authenticated header
These 3 arguments can be written or imported, but must given before using the I encrypt
block:
{! examples/SYM02.zen !}
The output is returned in secret message
and it looks like:
{"secret_message":{"iv":"u64:-tU2gbox9kATCeC2k_zkhYM-PBA3IzvN7HtfyVXdzB4",
"header":"u64:dGhpc19pc19mb3JfQm9i",
"text":"u64:cw4M3FBO3zaPRAB26d6y8SMPGgAo_0AmJUrhg5dmKwoEB7BWLAAD_A2h",
"checksum":"u64:UugLrIuxRX46BETc1-XkrA"}}
To decode make sure to have that secret password and that a valid secret message
is given, then use:
{! examples/SYM03.zen !}
So let’s imagine I want to share a secret with someone and send secret messages encrypted with it:
Of course the password must be known by all participants and that’s the
dangerous part, since it could be stolen.
We mitigate this risk using public-key cryptography, also known as
a-symmetric encryption, explained below.
Asymmetric encryption
We use asymmetric encryption (or public key
cryptography)
when we want to introduce the possession of keypairs (public and private) both by
Alice and Bob: this way there is no need for a single secret to be known to both.
Fortunately it is pretty simple to do using Zencode in 2 steps
Key generation and exchange
In this phase each participant will create his/her own keypair, store it and communicate the public key to the other.
The statement to generate a keypair (public and private keys) is simple:
{! examples/AES01.zen !}
It will produce something like this:
"Alice": {
"keypair": {
"private_key": "u64:F_NaS3Y6Xw6BW...",
"public_key": "u64:BLG0OGDwzP_gY41TZgGpUB4lTYCgpx9BJVScxSQAfwqEi..."
}
}
Where the public key is usually a longer octet and actually an Elliptic Curve Point coordinate.
There is nothing preventing an host application to separate these JSON
fields and store them in any secure way.
Here we demonstrate how to create keypairs as well separate them using
Zencode:
- 2 contracts to create Alice and Bob keypairs
- 2 contracts to separate the public key from the private key for each
After both Alice and Bob have their own keypairs and they both know
each other public key we can move forward to do asymmetric encryption
and signatures.
{! examples/AES01.zen !}
"Alice": {
"keypair": {
"private_key": "u64:F_NaS3Y6Xw6BW...",
"public_key": "u64:BLG0OGDwzP_gY41TZgGpUB4lTYCgpx9BJVScxSQAfwqEi..."
}
}
{! examples/AES02.zen !}
"Alice": {
"public_key": "u64:BLG0OGDwzP_gY41TZgGpUB4lTYCgpx9BJVScxSQAfwqEi..."
}
The advantage of using Zencode here is the use of the valid
keyword which effectively parses the public key
object and verifies it as valid, in this case as being a valid point on the elliptic curve in use. This greatly reduces the possibility of common mistakes.
Public-key Encryption (ECDH)
Public key encryption is similar to the asymmetric
encryption explained in the previous section,
with a difference: the from
and for
clauses indicating the public
key of the recipient.
Before getting to the encryption 2 other objects must be given:
keypair
is one’s own public and private keyspublic key
from the intended recipient
So with an input separated between DATA and KEYS or grouped together in an array like:
[
{"Bob": {"public_key":"u64:BGF59uMP0DkHoTjMT..."} },
{"Alice": { "keypair": {
"private_key": "u64:F_NaS3Y6Xw6BW...",
"public_key": "u64:BLG0OGDwzP_gY41TZgGpUB4lTYCgpx9BJVScxSQAfwqEi..." } } }
]
{! examples/AES05.zen !}
which encrypts and stores results in secret message
; also in this case header
may be given, then is included in the encryption as an authenticated clear-text section.
1. Alice encrypts the message using Bob’s public key
{! examples/AES05.zen !}
2. Bob prepares a keyring with Alice’s public key
{! examples/AES06.zen !}
3. Bob decrypts the message using Alice’s public key
{! examples/AES07.zen !}
In this basic example the session key for encryption is made combining
the private key of Alice and the public key of Bob (or
vice versa).
When I write 'my secret for you' in 'message'
and I write 'an authenticated message' in 'header'
The decryption will always check that the header hasn’t changed,
maintaining the integrity of the string which may contain important
public information that accompany the secret.
Public-key Signature (ECDSA)
Public-key signing allows to verify the integrity of a message by
knowing the public key of all those who have signed it.
It is very useful when in need of authenticating documents: any change
to the content of a document, even one single bit, will make the
verification fail, showing that something has been tampered with.
The one signing only needs his/her own keypair, so the key setup will
be made by the lines:
Given that I am known as 'Alice'
and I have my valid 'keypair'
then assuming that the document to sign is in draft
, Alice can
proceed signing it with:
and I create the signature of 'draft'
which will produce a new object signature
to be printed along the
draft: the original message stays intact and the signature is detached.
On the other side Bob will need Alice’s public key to verify the
signature with the line:
When I verify the 'draft' is signed by 'Alice'
which will fail in case the signature is invalid or the document has
been tampered with.
Here we continue assuming that the keyrings are already prepared with
public/private keypairs and the public keypair of the correspondent.
1. Alice signs a message for Bob
{! examples/DSA01.zen !}
1. Bob verifies the signed message from Alice
{! examples/DSA02.zen !}
In this example Alice uses her private key to sign and authenticate a
message. Bob or anyone else can use Alice’s public key to prove that
the integrity of the message is kept intact and that she signed it.
Advanced cryptographic flows in Zenroom
Attribute Based Credentials
Attribute Based Credentials are a powerful and complex feature
implemented using the Coconut crypto
scheme. This is the most
complex functionality available in Zenroom and it will show how the
Zencode language really simplifies it.
Let’s imagine 3 different subjects for our scenarios:
- Mad Hatter is a well known issuer in Wonderland
- Wonderland is an open space (a blockchain!) and all inhabitants can check the validity of proofs
- Alice just arrived: to create proofs she’ll request a credential to the issuer MadHatter
When Alice is in possession of credentials then she can
create a proof any time she wants using as input:
- the credentials
- her credential keypair
- the verifier by MadHatter
{! examples/create_proof.zen !}
All these "things" (credentials, proofs, etc.) are data structures that can be used as input and received as output of Zencode functions. For instance a proof can be print in JSON format and looks a bit list this:
{
"credential_proof" : {
"pi_v" : {
"c" : "u64:tBrCGawWYEAi55_hHIPq0JT3OaapOebSHVW0GhjJcAk",
"rr" : "u64:J7R3FXsI2dcfyZRCqWA8fDYijG39P16LvGpX90wtCWw",
"rm" : "u64:QoG-28CNTAY3Ir4SQqVoK1ZpTlzOnXxX6Xtq5KMIxpo"
},
"nu" : "u64:BA77WYvBRsc53uAyrqTjuUdptJPZbcTlzr9icizm0...",
"sigma_prime" : {
"h_prime" : "u64:BB9AM5xjWPxsZ47zh1WAmFymru66W6YuK...",
"s_prime" : "u64:BAGYNM6JO0wRAGE87_-bQVuhUXeEoeJrh..."
},
"kappa" : "u64:GFVYsudbHOJNzPl3ZL0_VzB_DRvrPKF26OCZR9..."
},
"zenroom" : {
"scenario" : "coconut", "encoding" : "url64", "version" : "1.0.0"
}
}
Anyone can verify proofs using as input:
- the credential proof
- the verifier by MadHatter
{! examples/verify_proof.zen !}
What is so special about these proofs? Well! Alice cannot be followed
by her trail of proofs: she can produce an infinite number of
proofs, always different from one another, for anyone to recognise
the credential without even knowing who she is.
Imagine that once Alice is holding credentials she can enter
any room in Wonderland and drop a proof in the chest at its
entrance: this proof can be verified by anyone without disclosing
Alice’s identity.
The flow described above is pretty simple, but the steps to setup the
credential are a bit more complex. Lets start using real names
from now on:
- Alice is a credential Holder
- MadHatter is a credential Issuer
- Wonderland is a public Blockchain
- Anyone is any peer connected to the blockchain
To add more detail, the sequence is:
1 MadHatter generates an issuer keypair
Input | Zencode | Output |
---|---|---|
– | {! examples/issuer_keygen.zen !} | issuer_keypair |
1a MadHatter publishes the verification key
Input | Zencode | Output |
---|---|---|
issuer_keypair | {! examples/publish_verifier.zen !} | issuer_verifier |
2 Alice generates her credential keypair
Input | Zencode | Output |
---|---|---|
– | {! examples/credential_keygen.zen !} | credential_keypair |
3 Alice sends her credential signature request
Input | Zencode | Output |
---|---|---|
credential_keypair | {! examples/create_request.zen !} | credential_request |
4 MadHatter decides to sign a credential signature request
Input | Zencode | Output |
---|---|---|
credential_request issuer_keypair | {! examples/issuer_sign.zen !} | issuer_signature |
5 Alice receives and aggregates the signed credential
Input | Zencode | Output |
---|---|---|
issuer_signature credential_keypair | {! examples/aggregate_signature.zen !} | credential |
Centralized credential issuance
Lets see how flexible is Zencode.
The flow described above is for a fully decentralized issuance of
credentials where only the Holder is in possession of the
credential keypair needed to produce a credential proof.
But let’s imagine a much more simple use-case for a more centralized
system where the Issuer provides the Holder with everything
ready to go to produce zero knowledge credential proofs.
The implementation is very, very simple: just line up all the When
blocks where the different operations are done at different times and
print the results all together!
Scenario coconut
Given that I am known as 'Issuer'
When I create the issuer keypair
and I create the credential keypair
and I create the credential request
and I create the credential signature
and I create the credentials
Then print the 'credentials'
and print the 'credential keypair'
This will produce credentials that anyone can take and run. Just
beware that in this simplified version of ABC the Issuer may
maliciously keep the credential keypair and impersonate the
Holder.
Try it on your system!
Impatient to give it a spin? run Zencode scripts locally to see what
are the files produced!
Make sure that Zenroom is installed on your PC
and then go to the…
Zero Knowledge Proofs
There is more to this of course: Zencode supports several features
based on pairing elliptic curve arithmetics and in particular:
- non-interactive zero knowledge proofs (also known as ZKP or ZK-Snarks)
- threshold credentials with multiple decentralised issuers
- homomorphic encryption for numeric counters
These are all very useful features for architectures based on the
decentralisation of trust, typical of DLT and blockchain based
systems, as well for off-line and non-interactive authentication.
The Zencode language leverages two main scenarios, more will be
implemented in the future.
- Attribute Based Credentials (ABC) where issuer verification keys
represent specific credentials - A Petition system based on ABC and homomorphic encryption
Three more are in the work and they are:
- Anonymous proxy validation scheme
- Token thumbler to privately transfer numeric assets
- Private credential revocation
Import, validate and transform data
Given
Self introduction
This affects my statements
Given I introduce myself as ''
Given I am known as ''
Given I am ''
Given I have my ''
Given I have my valid ''
Data provided as input (from data and keys) is all imported
automatically from JSON or CBOR binary formats.
Scenarios can add Schema for specific data validation mapped to words like: signature, proof or secret.
Data input
Given I have a ''
Given I have a valid ''
Given I have a '' inside ''
Given I have a valid '' inside ''
Given I have a '' from ''
Given I have a valid '' from ''
Given the '' is valid
or check emptiness:
Given nothing
When valid is specified then extra checks are made on input value,
mostly according to the scenario
Settings
rule input encoding [ url64 | base64 | hex | bin ]
rule input format [ json | cbor ]
When
Processing data is done in the when block. Also scenarios add statements to this block.
Without extensions, these are the basic functions available
when:
- {when: 'I append '''' to '''''}
- {when: 'I write '''' in '''''}
- {when: 'I set '''' to '''''}
- {when: 'I create a random '''''}
- {when: 'I create a random array of '''' elements'}
- {when: 'I create a random '''' bit array of '''' elements'}
- {when: 'I set '''' as '''' with '''''}
- {when: 'I append '''' as '''' to '''''}
- {when: 'I write '''' as '''' in '''''}
Then
Output is all exported in JSON or CBOR
then:
- {then: 'print '''' '''''}
- {then: 'print all data'}
- {then: 'print my data'}
- {then: 'print my data'}
- {then: 'print my '''''}
- {then: 'print as '''' my '''''}
- {then: 'print my '''' as '''''}
- {then: 'print the '''''}
- {then: 'print as '''' the '''''}
- {then: 'print as '''' the '''' inside '''''}
Settings:
rule output encoding [ url64 | base64 | hex | bin ]
rule output format [ json | cbor ]
Crypto modeling with Zenroom and Lua
The Zenroom VM uses the Lua direct-syntax parser to securely interpret
and execute operations including complex arithmetics on Elliptic Curve
primitives ECP module as well Pairing
operations on twisted curves (ECP2).
The resulting scripting language is a restricted Lua dialect
without any external extension, customised to resemble as much as
possible the scripting language used by cryptographers in software, as
for instance Mathematica.
With Zenroom we want to lower the friction that cryptographers face
when implementing new crypto models. One can see this software as a
sort of templating system bridging the work of cryptographers and
programmers.
The Zencode
Whitepaper
explains in depth the issues at stake.
The intended audience of this documentation chapter are
cryptographers.
Short path from math to production
Examples speak more than a thousand words. We will dive into two
implementations to make it evident how easy is to go from an
academic paper to a portable implementation running efficiently on
any platform.
ElGamal
As a basic introduction we propose the implementation of ElGamal
encryption
system. The code
below makes use of the ECP arithmetics
provided by Zenroom to produce ElGamal commitments useful to
zero-knowledge proofs.
G = ECP.generator()
O = ECP.order()
salt = ECP.hashtopoint("Constant random string")
secret = INT.new(sha256("Secret message to be hashed to a number"))
r = INT.modrand(O)
commitment = G * r + salt * secret
-- keygen
seckey = INT.modrand(O)
pubkey = seckey * G
-- sign
k = INT.modrand(O)
cipher = { a = G * k,
b = pubkey * k + commitment * secret }
-- verify
assert(cipher.b - cipher.a * seckey
==
commitment * secret)
One can play around with this code already by using our online demo.
BLS signatures
The pairing property of some elliptiv curves can be exploited for
short signatures as defined by Boneh-Lynn-Schacham
(BLS)
in 2001.
Here the Zenroom implementation:
msg = str("This is the authenticated message")
G1 = ECP.generator()
G2 = ECP2.generator()
O = ECP.order()
-- keygen: δ = r.O ; γ = δ.G2
sk = INT.modrand(O)
pk = G2 * sk
-- sign: σ = δ * ( H(msg)*G1 )
sm = ECP.hashtopoint(msg) * sk
-- verify: ε(γ,H(msg)) == ε(G2,σ)
hm = ECP.hashtopoint(msg)
assert( ECP2.miller(pk, hm) == ECP2.miller(G2, sm),
"Signature doesn't validates")
One-round tripartite shared secret
This secret sharing protocol uses BLS curve pairing in a rather simple way, it was first described by Antonine Joux in the paper A One Round Protocol for Tripartite Diffie–Hellman (2000).
Here the Zenroom demonstration of the protocol:
-- Joux’s one-round Tripartite Diffie-Hellman
-- Setup
local G1 = ECP.generator()
local G2 = ECP2.generator()
local O = ECP.order()
-- Parties A,B,C generate random a,b,c ∈ Zr
a = INT.modrand(O)
b = INT.modrand(O)
c = INT.modrand(O)
-- Parties A,B,C broadcast to all aG, bG, cG
aG1 = G1 * a
aG2 = G2 * a
bG1 = G1 * b
bG2 = G2 * b
cG1 = G1 * c
cG2 = G2 * c
-- Theoretical proof of ε(G, G)^abc
K = ECP2.miller(G2, G1) ^ ( a * b * c )
-- A computes KA = ε(bG, cG)^a
KA = ECP2.miller(bG2, cG1) ^ a
-- B computes KB = ε(aG, cG)^b
KB = ECP2.miller(aG2, cG1) ^ b
-- C computes KC = ε(aG, bG)^c
KC = ECP2.miller(aG2, bG1) ^ c
-- Shared key is K = KA = KB = KC
assert(K == KA)
assert(K == KB)
assert(K == KC)
ECQV
For a practical example we will now use the Zenroom implementation of
the Elliptic Curve Qu-Vanstone
(ECQV) scheme also known as
"Implicit
Certificate" and
widely used by Blackberry technologies.
Mathematical Formula
Zenroom Implementation
G = ECP.generator()
function rand() -- random modulo
return BIG.modrand(ECP.order())
end
-- make a request for certification
ku = BIG.modrand(ECP.order())
Ru = G * ku
-- keypair for CA
dCA = BIG.modrand(ECP.order()) -- private
QCA = G * dCA -- public (known to Alice)
-- from here the CA has received the request
k = BIG.modrand(ECP.order())
kG = G * k
-- public key reconstruction data
Pu = Ru + kG
declaration =
{ public = Pu:octet(),
requester = str("Alice"),
statement = str("I am stuck in Wonderland.") }
declhash = sha256(OCTET.serialize(declaration))
hash = BIG.new(declhash, ECP.order())
-- private key reconstruction data
r = (hash * k + dCA) % ECP.order()
-- verified by the requester, receiving r,Certu
du = (r + hash * ku) % ECP.order()
Qu = Pu * hash + QCA
assert(Qu == G * du)
Elliptic Curve Point arithmetics
The brief demonstration above shows how easy it can be to implement a
cryptographic scheme in Zenroom’s Lua dialect, which gives immediately
the advantage of a frictionless deployment on all targets covered by
our VM.
Arithmetic operations also involving Elliptic Curve Points
(ECP) are applied using simple operators on
BIG integers.
All this is possible without worrying about library dependencies, OS
versioning, interpreter’s availability etc.
Main advantages of this approach
Putting the mathematical formula and the code side by side while using
the same variable names greatly helps to review the correctness of the
implementation.
The tooling inherited from Zenroom allows to swiftly build test
coverage and benchmarks.
Future Zenroom developments will improve the provability of the
calculations and their results, as well provide testing techniques as
fuzzing: this will automatically benefit all implementations.
Cryptographers can work independently from programmers, by modeling
their formulas using their best known tools and then provide the
script as a payload to be uploaded inside the VM.
System integrators can work on embedding Zenroom as a VM
without worrying about cryptographic libraries APIs and moving
dependencies required by the cryptographic implementations.
All in all, by using Zenroom your cryptographic model implementation
is ready for an accellerating future where crypto technologies will
grow in diversity and possibilities!
More reference here: https://dev.zenroom.org/lua/