This repository is meant to hold all the necessary smart contracts for the DDDC Barcelona Pilot. The smart contracts are written in zencode and will run over zenroom.
The main use case for Zenroom is that of distributed computing of untrusted code where advanced cryptographic functions are required, for instance it can be used as a distributed ledger implementation (also known as blockchain smart contracts).
Zenroom is a software in BETA stage and are part of the DECODE project about data-ownership and technological sovereignty. Our effort is that of improving people's awareness of how their data is processed by algorithms, as well facilitate the work of developers to create along privacy by design principles using algorithms that can be deployed in any situation without any change.
🚩 Table of Contents (click to expand)
There are 8 script/contracts involved in the coconut credential process, but mainly we can divide them in two macro step:
- Provisioning of the credential (Script 1-6) involves the citizen and the credential issuer (to run once)
- Verification of blind proof credentials (Script 7, 8) involves the citizen and a verifier/checker
The provisioning is structured as follow:
- The citizen creates a
request blind signature
(Script 1, 2) that should be sent to the credential issuer - The credential issuer validates and signs the
request blind signature
and generates ablind signed credential
that should be sent back to the citizen (Scripts 3-5) - The citizen with the
blind signed credential
generates the realcredential
that is verified by the credential issuer, that should be stored (Script 6)
The second step is the verification of blind proof that is structured as follows:
- Once the citizen holds a credential, could generate
blind proof of credentials
by using his secret info and credential issuer's public info (Script 7) and send to a checker/verifier - A checker/verifier can verify the
blind proof of credentials
returning true or fail (Script 8)
🔣 INPUT PARAMS | ⬇️ DATA | 🔐 KEYS | 📃 OUPUT |
---|---|---|---|
identifier | 🚫 | 🚫 | Yes (e.g. keypair.keys) |
Scenario 'coconut': "To run over the mobile wallet the first time and store the output as keypair.keys"
Given that I am known as 'identifier'
When I create my new keypair
Then print all data
This script should be run once, and the output should be saved in a secure place, it will contain the secret and public keyring of the citizen
🏃♀️ Expected result format
{"identifier":{"public":"...","private":"..."}}
🔣 INPUT PARAMS | ⬇️ DATA | 🔐 KEYS | 📃 OUPUT |
---|---|---|---|
identifier | 🚫 | output of 01-CITIZEN-credential-keygen | Yes (e.g. blind_signature.req) |
Scenario 'coconut': "To run after the request keypair is stored (keypair.keys)"
Given that I am known as 'identifier'
and I have my credential keypair
When I request a blind signature of my keypair
Then print all data
This contract creates a blind signature request to send to the credential issuer.
The previous saved keypair from 01-CITIZEN-request-keypair.zencode should be passed as the KEYS
and the identifier should be the same of the one stored in such KEYS
.
The output should be sent to the credential issuer to be signed.
🏃♀️ Expected result format
{
"request": {
"pi_s": {
"rk": "...",
"c": "...",
"rr": "...",
"rm": "..."
},
"c": {
"b": "...",
"a": "..."
},
"cm": "...",
"public": "..."
}
}
🔣 INPUT PARAMS | ⬇️ DATA | 🔐 KEYS | 📃 OUPUT |
---|---|---|---|
issuer_identifier | 🚫 | 🚫 | Yes (e.g. issuer_keypair.keys) |
Scenario 'coconut': Generate credential issuer keypair
Given that I am known as 'issuer_identifier'
When I create my new issuer keypair
Then print all data
This script should be run once, and the output should be saved in a secure place, it will contain the secret and public keyring of the credential_issuer
🏃♀️ Expected result format
{
"issuer_identifier":{
"sign":{
"y":"...",
"x":"..."
},
"verify":{
"beta":"...",
"g2":"...",
"alpha":"..."
}
}
}
🔣 INPUT PARAMS | ⬇️ DATA | 🔐 KEYS | 📃 OUPUT |
---|---|---|---|
issuer_identifier | 🚫 | output of 03-CREDENTIAL_ISSUER-keygen | Yes (e.g. issuer_verifier.keys) |
Scenario 'coconut': "Publish the credential issuer verification key"
Given that I am known as 'issuer_identifier'
and I have my issuer keypair
When I publish my issuer verification key
Then print all data
This contract prints the public part of the verification keypair of the credential issuer, that should be available to the checker
🏃♀️ Expected result format
{
"issuer_identifier":{
"verify":{
"beta":"...",
"g2":"...",
"alpha":"..."
}
}
}
🔣 INPUT PARAMS | ⬇️ DATA | 🔐 KEYS | 📃 OUPUT |
---|---|---|---|
issuer_identifier | output of 02-CITIZEN-credential-request | output of 03-CREDENTIAL_ISSUER-keygen | Yes (e.g. issuer_signed_credential.json) |
Scenario 'coconut': "To run by the credential issuer and store the output as ci_signed_credential.json"
Given that I am known as 'issuer_identifier'
and I have my issuer keypair
When I am requested to sign a credential
and I verify the credential to be true
and I sign the credential
Then print all data
This contract takes as DATA the output of 02-CITIZEN-request-blind-signature.zencode and the secret keypair of the credential issuer and emits a signed credential that should be send back to the citizen.
🏃♀️ Expected result format
{
"issuer_identifier": {
"a_tilde": "...",
"schema": "coconut_sigmatilde",
"b_tilde": "...",
"version": "0.8.1",
"h": "..."
}
"verifiy": {
"alpha" : "...",
"curve" : "bls383",
"schema" : "issue_verify",
"encoding" : "hex",
"zenroom" : "0.8.1",
"beta" : "..."
}
}
🔣 INPUT PARAMS | ⬇️ DATA | 🔐 KEYS | 📃 OUPUT |
---|---|---|---|
identifier | output of 05-CREDENTIAL_ISSUER-credential-sign | output of 01-CITIZEN-credential-keygen | Yes (e.g. credential.json) |
Scenario 'coconut': "To run by citizen and store the output as credential.json"
Given that I am known as 'identifier'
and I have my credential keypair
When I receive a credential signature 'issuer_identifier'
and I aggregate the credential into my keyring
Then print all data
This is the last step of the provisioning of the citizen. This contract creates a blind signed credential to be stored in a secure place.
🏃♀️ Expected result format
{
"name": "identifier",
"credential": {
"h": "...",
"schema": "coconut_aggsigma",
"version": "0.8.1",
"s": "..."
}
}
🔣 INPUT PARAMS | ⬇️ DATA | 🔐 KEYS | 📃 OUPUT |
---|---|---|---|
identifier | output of 04-CREDENTIAL_ISSUER-publish-verifier | output of 06-CITIZEN-aggregate-credential-signature | Yes (e.g. blindproof_credential.json) |
issuer_identifier |
Scenario 'coconut': "To run by citizen and send the result blindproof_credential.json to the verifier/checker"
Given that I am known as 'identifier'
and I have my credential keypair
and I use the verification key by 'issuer_identifier'
and I have a signed credential
When I aggregate all the verification keys
and I generate a credential proof
Then print all data
This is run by the citizen to create a valid blind proof of the credentials, should be then send to the checker/verifier
🏃♀️ Expected result format
{
"proof": {
"schema": "coconut_theta",
"sigma_prime": {
"s_prime": "...",
"h_prime": "..."
},
"pi_v": {
"rm": "...",
"rr": "...",
"c": "..."
},
"version": "0.8.1",
"nu": "...",
"kappa": "..."
}
}
🔣 INPUT PARAMS | ⬇️ DATA | 🔐 KEYS | 📃 OUPUT |
---|---|---|---|
issuer_identifier | output of 04-CREDENTIAL_ISSUER-publish-verifier | output of 07-CITIZEN-prove-credential | 🚫 |
Scenario 'coconut': "To run by the checker and prints OK in stout if verified"
Given that I use the verification key by 'issuer_identifier'
and that I have a valid credential proof
When I aggregate all the verification keys
and the credential proof is verified correctly
Then print string 'OK'
This verifies if a blind proof of credential is valid, in a success case just prints OK
to stdout
🔣 INPUT PARAMS | ⬇️ DATA | 🔐 KEYS | 📃 OUPUT |
---|---|---|---|
identifier | output of 04-CREDENTIAL_ISSUER-publish-verifier.zencode | output of 06-CITIZEN-aggregate-credential-signature.zencode | Yes (e.g. petition_request.json) |
issuer_identifier | |||
petition (Pe) |
Scenario 'coconut': "Citizen creates a new petition, using his own key, the credential and the credential issuer's public"
Given that I am known as 'identifier'
and I have my keypair
and I have a signed credential
and I use the verification key by 'issuer_identifier'
When I aggregate all the verification keys
and I generate a credential proof
and I create a new petition 'petition'
Then print all data
Citizen creates a new petition, using his own key, the credential and the credential issuer's public.
🏃♀️ Expected result format
{
"petition": {
"owner": "...",
"schema": "petition",
"scores": {
"neg": {
"left": "Infinity",
"right": "Infinity"
},
"pos": {
"left": "Infinity",
"right": "Infinity"
},
},
"uid": "...",
},
"petition_ecdh_sign": ["..."],
"proof": {
"kappa": "",
"nu": "",
"pi_v": {
"c": "",
"rm": "",
"rr": ""
},
"schema": "theta",
"sigma_prime": {
"h_prime": "",
"s_prime": ""
},
},
"verifier": {
"alpha": "",
"beta": "",
"schema": "issue_verify",
}
}
🔣 INPUT PARAMS | ⬇️ DATA | 🔐 KEYS | 📃 OUPUT |
---|---|---|---|
issuer_identifier | output of 09-CITIZEN-create-petition.zencode | output of **04-CREDENTIAL_ISSUER-publish-verifier.zencode ** | Yes (e.g. petition.json) |
Scenario 'coconut': "Approve the creation of a petition: executed by a Citizen, using several keys"
Given that I use the verification key by 'issuer_identifier'
and I receive a new petition request
When I aggregate all the verification keys
and I verify the new petition to be valid
Then print all data
Approve the creation of a petition: executed by a Citizen, using several keys.
🏃♀️ Expected result format
{
"petition": {
"list": {
"...": true,
"...": true,
"...": true
},
"owner": "...",
"schema": "petition",
"scores": {
"neg": {
"left": "",
"right": ""
},
"pos": {
"left": "",
"right": ""
},
},
"uid": "...",
},
"verifier": {
"alpha": "",
"beta": "",
"schema": "issue_verify",
}
}
🔣 INPUT PARAMS | ⬇️ DATA | 🔐 KEYS | 📃 OUPUT |
---|---|---|---|
identifier | output of 04-CREDENTIAL_ISSUER-publish-verifier.zencode | output of 06-CITIZEN-aggregate-credential-signature.zencode | Yes (e.g. petition_signature.json) |
issuer_identifier | |||
petition (Pe) |
Scenario 'coconut': "Sign petition: the Citizen signs the petition, with his own keys, aggregating it with the credential and with the Credential Issuer public key"
Given that I am known as 'identifier'
and I have my keypair
and I have a signed credential
and I use the verification key by 'issuer_identifier'
When I aggregate all the verification keys
and I sign the petition 'petition'
Then print all data
Sign petition: the Citizen signs the petition, with his own keys, aggregating it with the credential and with the Credential Issuer public key.
🏃♀️ Expected result format
{
"petition_signature": {
"proof": {
"kappa": "...",
"nu": "...",
"pi_v": {
"c": "...",
"rm": "...",
"rr": "..."
},
"schema": "theta",
"sigma_prime": {
"h_prime": "...",
"s_prime": "..."
},
},
"uid_petition": "...",
"uid_signature": "..."
}
}
🔣 INPUT PARAMS | ⬇️ DATA | 🔐 KEYS | 📃 OUPUT |
---|---|---|---|
🚫 | output of 11-CITIZEN-sign-petition.zencode | output of 10-VERIFIER-approve-petition.zencode | Yes (e.g. petition-increase.json) |
Scenario 'coconut': "Add a petition to the ledger (count): here the petition signature created by the citizen is aggregated with the petition and written to the ledger, no encryption is used"
Given that I receive a signature
and I receive a petition
When a valid petition signature is counted
Then print all data
Add a petition to the ledger (count): here the petition signature created by the citizen is aggregated with the petition and written to the ledger, no encryption is used.
🏃♀️ Expected result format
{
"petition_signature": {
"proof": {
"kappa": "...",
"nu": "...",
"pi_v": {
"c": "...",
"rm": "...",
"rr": "..."
},
"schema": "theta",
"sigma_prime": {
"h_prime": "...",
"s_prime": "..."
},
},
"uid_petition": "...",
"uid_signature": "..."
}
}
🔣 INPUT PARAMS | ⬇️ DATA | 🔐 KEYS | 📃 OUPUT |
---|---|---|---|
identifier | output of 12-LEDGER-add-signed-petition.zencode | output of 06-CITIZEN-aggregate-credential-signature.zencode | Yes (e.g. tally.json) |
Scenario 'coconut': "Close the petition, formally 'the tally': this contract adds the final block to the ledger, making it impossible to sign petition after this has happened. It's also impossible to count the signatures without having the petition tallied"
Given that I am known as 'identifier'
and I have my keypair
and I receive a petition
When I tally the petition
Then print all data
Close the petition, formally 'the tally': this contract adds the final block to the ledger, making it impossible to sign petition after this has happened. It's also impossible to count the signatures without having the petition tallied.
🏃♀️ Expected result format
{
"petition": {
"owner": "...",
"schema": "petition",
"scores": {
"neg": {
"left": "...",
"right": "..."
},
"pos": {
"left": "...",
"right": "..."
},
},
"uid": "..."
},
"tally": {
"c": "...",
"dec": {
"neg": "...",
"pos": "..."
},
"rx": "...",
"schema": "petition_tally",
"uid": "..."
}
}
🔣 INPUT PARAMS | ⬇️ DATA | 🔐 KEYS | 📃 OUPUT |
---|---|---|---|
🚫 | output of 11-CITIZEN-sign-petition.zencode | output of 13-CITIZEN-tally-petition.zencode | 🚫 |
Scenario 'coconut': "Count the petition results: any Citizen can count the petition as long as they have the 'tally'"
Given that I receive a petition
and I receive a tally
When I count the petition results
Then print all data
Count the petition results: any Citizen can count the petition as long as they have the 'tally'.
🔣 INPUT PARAMS | ⬇️ DATA | 🔐 KEYS | 📃 OUPUT |
---|---|---|---|
🚫 | content you want to hash | 🚫 | HASH |
print(ECDH.kdf(HASH.new('sha512'), DATA))
This script make a unique hash of the content passed in DATA. The hashing algorithm is a sha512 with KDF, to prevent bruteforcing, hence could be not immediate.
This has to be run oven all personal information, before sending to the credential issuer.
🔣 INPUT PARAMS | ⬇️ DATA | 🔐 KEYS | 📃 OUPUT |
---|---|---|---|
issuer_identifier | output of 09-CITIZEN-create-petition.zencode | Yes (e.g. petition_validatin.json) |
Scenario 'coconut': "Validate a petition: Executed by the ledger in the validation phase to check that a petition is good to store. Idempotent."
Given that I receive a petition
Then print all data
Validate the structure of the petition. Currently this does no cryptographic validation, just checks that the petition is the correct data structure.
We can use this in ledger implementations that require a deterministic verification step, for example in our zentooth transaction processor for SawTooth.
🏃♀️ Expected result format
{
}
docker build -t dddc-pilot-contracts .
docker run --rm -it dddc-pilot-contracts
To run the zencode
contracts in a more verbose way, you should change the source code
ZEN:begin(1)
to reset it to silent mode change with
ZEN:begin(0)
Copyright ©️ 2019 by Dyne.org foundation, Amsterdam
Designed, written and maintained by Puria Nafisi Azizi and Denis "Jaromil" Rojo.
Special thanks to Andrea "Bario" D'Intino for his expert reviews.
This project is receiving funding from the European Union’s Horizon 2020 research and innovation programme under grant agreement nr. 732546 (DECODE).
https://www.decidim.barcelona/
DDDC Smart Contracts
Copyright :copyright: 2019 Dyne.org foundation, Amsterdam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.