diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..707c88c --- /dev/null +++ b/.gitignore @@ -0,0 +1,237 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + + +# Created by https://www.gitignore.io/api/emacs + +### Emacs ### +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile +projectile-bookmarks.eld + +# directory configuration +.dir-locals.el + +# saveplace +places + +# url cache +url/cache/ + +# cedet +ede-projects.el + +# smex +smex-items + +# company-statistics +company-statistics-cache.el + +# anaconda-mode +anaconda-mode/ + +# End of https://www.gitignore.io/api/emacs + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +.static_storage/ +.media/ +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# pip installation directory +src/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d860ca9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM node:9.10.1 +MAINTAINER TzLibre "tzlibre@mail.com" + +COPY gen-signatures.sh /tmp +ADD pykeychecker /tmp/pykeychecker/ +ADD jstxencoder /tmp/jstxencoder/ +RUN wget https://download.libsodium.org/libsodium/releases/libsodium-1.0.16.tar.gz; \ + tar xfz libsodium-1.0.16.tar.gz; \ + cd libsodium-1.0.16; \ + ./configure; \ + make -j2; \ + make install +RUN apt-get update; apt-get -y install python-dev python-pip + +WORKDIR /tmp +RUN cd pykeychecker; pip install -r requirements.txt +RUN cd jstxencoder; npm install + +CMD bash gen-signatures.sh diff --git a/README.md b/README.md index 9cc4ace..066c938 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,298 @@ -# fundraiser-tools -Tools for checking keys created in the Tezos fundraiser +# Command-line tools + +This repository contains **Command-line tools** to ease [whitelisting](https://tzlibre.github.io/whitelist.html) for the [TzLibre](https://tzlibre.github.io) split. + +These tools are based on a fork of DLS [`fundraiser-tools`](https://github.com/tezos/fundraiser-tools). + +The **Command-line tools** support you in: + +1. generating two digital signatures (namely `ETH_addrSignature` and `declarationSignature`) to prove ownership of a Tezos private key and therefore the right to receive TZL coins; +2. serializing both signatures in a single `tx-data` field to be broadcast into an Ethereum transaction according to the instructions provided on the [TzLibre website](https://tzlibre.github.io/whitelist.html#send-tx). + +To improve usability and security a sandboxed execution environment is provided as a Docker container, along with an invocation bash script. + +## Security + +As a standard security practice when handling sensitive data you must: +1. Read the whole source beforehand and be fully responsible for the code you run on your computer. +2. Verify the computer you're using is secure and not compromised. + +## Table of Content + +- [Declaration](#declaration): description and content of the declaration +- [Repository content](#repository-content): description of the directories and files of the repository +- [Signatures generation](#signatures-generation): instructions to generate your `ETH_addrSignature` and `declarationSignature`, and to serialize them inside a sandboxed environment. +- [Tech details](#tech-details): technical details of the tools used in the sandboxed execution environment. + +## Declaration + +Whitelisting involves cryptografically signing a declaration to support decoupling the Tezos idea from its implementations. You must read, understand and agree with the declaration before signing it. Signatures are pseudonymous and not linked with your identity. You should not sign the declaration if you don't fully understand and agree with its content. + +> Declaration: `I hereby cryptographically prove to be a contributor of Tezos Stiftung (CHE-290.597.458), a Swiss Foundation based in Gubelstrasse 11, 6300 Zug, Switzerland. I recognize and welcome the existence multiple implementations of Tezos. I ask and expect Tezos Stiftung to foster competition among them by funding and supporting their development, marketing and growth. Funds allotted to various Tezos implementations shall always be directly proportional to their market capitalization at the time of each distribution of funds. Distribution of funds to multiple existing Tezos implementations shall begin no later than January 1st 2019 and consistently continue throughout time. Following priorities autonomously set by each community, Tezos Stiftung shall distribute funds in the most appropriate, effective and transparent way.` + + +## Repository content + +This repository contains the following directories and files: + +- `Dockerfile`: instructions to create the Docker-based sandboxed execution environment. A Docker environment increases safety and stability, and allows the user to avoid installing dependencies or configuring an environment. +- `gen-signatures.sh`: Bash script that improves usability and security by interactively asking the user for inputs while running the `pykeychecker` and `jstxencoder` tools. +- `pykeychecker`: written in Python, this is an extended version (you can verify the [4 diffs](https://github.com/tezos/fundraiser-tools/compare/master...tzlibre:master?diff=split&name=master]#diff-e6c8e5e03826917a611ce5c5e23626fc)) of the broken DLS [`fundraiser-tools`](https://github.com/tezos/fundraiser-tools) which it also fixes. This is the only part of the code that interacts with private and sensible data. It then generates signatures for the declaration and for your Ethereum address. +- `jstxencoder`: Node.js tool to serialize public key and signatures as a `tx-data` hexadecimal field. It allows to pass the three parameters to an Ethereum smart contract. + + +## Signatures generation + +### Inputs +This information is required: + +- Tezos address (aka public key hash). You can find it in the PDF containing your contribution data. +- 15 words secret. You can find them in the PDF containing your contribution data. +- email used when contributing to the Tezos ICO. You can find it in the PDF containing your contribution data. +- password set during Tezos ICO. +- an Ethereum address you want to whitelist. + +> You can whitelist an Ethereum address only once. The same Ethereum address can't be used to split multiple Tezos addresses. If you need to split several Tezos addresses you must to use a new Ethereum address each time. + +### Outputs +At the end of this process you'll get: + +- `ETH_addrSignature`: signature of your Ethereum address. +- `declarationSignature`: signature of the declaration (reported below). +- `tx-data`: a serialization of the previous two signatures, ready to be used as *transaction data* in a standard Ethereum transaction. + +### 1. Sandboxed execution environment generation +To improve security, simplify environment configuration and avoid installing dependencies, you can build a Docker image starting from our `Dockerfile`. + +The `Dockerfile` creates an environment and then prepares it to run `gen-signature.sh`. You can read the content of the file to verify its behavior. + +#### 1.1 Install Docker +You'll need a working Docker environment. Docker installation instructions can be found [here](https://www.docker.com/community-edition#/download). + +#### 1.2 Get the code + +Clone the **Command-line tools** repository from GitHub: + +```sh +git clone https://github.com/tz-libre/fundraiser-tools.git +``` + +then move to the repository folder: + +```sh +cd fundraiser-tools +``` + +#### 1.3 Build the `tzlibre/cl-tools` Docker image +Once you cloned the repository and have Docker up and running, you should issue: + +```sh +docker build . -t tzlibre/cl-tools +``` + +> Depending on your system configuration you might need to run Docker with admin privileges: (e.g. `sudo docker build . -t tzlibre/cl-tools`). + +At the end of the build, you should see this message: + +```sh +Successfully tagged tzlibre/cl-tools:latest +``` + +or + +```sh +Successfully built +``` + +These messages confirm the environment has been successfully built. + +### 2. Run Docker container to produce signatures + +You have now built a Docker image that can use the `gen-signatures.sh` script. + +> **THIS STEP WILL REQUIRE TO INPUT YOUR SENSITIVE INFORMATION. WE SUGGEST TO DISCONNECT FROM ALL NETWORKS BEFORE CONTINUING.** + +To generate signatures issue this command and follow on screen instructions: + +```sh +docker run -it --rm tzlibre/cl-tools +``` + +> According to your system configuration you might need to run Docker with admin privileges: (e.g. `sudo docker run -it --rm tzlibre/cl-tools`). + +Here is a full usage example: + +```sh +$ docker run -it --rm tzlibre/cl-tools + + ********* + * INPUT * + ********* + +15 Word Secret Key: +word1 word2 word word4 word5 word6 word7 word8 word9 word10 word11 word12 word13 word14 word15 + +Email: +your@email.com + +Password: +yourPassword + +Ethereum address to whitelist: +0x1234567890123456789012345678 + + + ********** + * OUTPUT * + ********** + +Tezos address (your public key hash): +tz1bG4r2PkzsxARQFnScVr51NaXrcVdF1Myg + +Tezos public key (`TZL_pk`): +012f1e1c8ed3eea205c75323c6db7f2a74b3273921c06a6629056331612d275e + +Signature of the Ethereum address (`ETH_addrSignature`): +1a9871ca4357ef82ab8b427e428e1f430c78755f4d15b826d89bfc57e0309e3f468b5ea5b73921138f9a5e3d132a995c7bacd5a8a50800589e29232382e66c0d + +Signature of the declaration (`declarationSignature`): +460323168a9b29629586ea888be344243a12003c381a9c427d2ebd94406f5e0376f1240c7d97b9acd686fa6003e10d72bdffc3f7ddeb3c7904d783a392ca490f + +Transaction data: +0x1d997f8b012f1e1c8ed3eea205c75323c6db7f2a74b3273921c06a6629056331612d275e000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000401a9871ca4357ef82ab8b427e428e1f430c78755f4d15b826d89bfc57e0309e3f468b5ea5b73921138f9a5e3d132a995c7bacd5a8a50800589e29232382e66c0d0000000000000000000000000000000000000000000000000000000000000040460323168a9b29629586ea888be344243a12003c381a9c427d2ebd94406f5e0376f1240c7d97b9acd686fa6003e10d72bdffc3f7ddeb3c7904d783a392ca490f +``` + +> You should now verify that `Tezos address` provided in output is the same you find in your Tezos contribution PDF. + +> Finally, you should copy & paste the `Transaction data` provided in output, and then save it as a plain text file. + +### 3. Signatures verification (optional) + +To verify correctness of the produced signatures you can use the [`signatures-verification-tool`](https://github.io/tzlibre/signatures-verification-tool). + +### 4. Broadcast + +You've now generated your `Transaction data`. + +You must now broadcast them by sending an Ethereum transaction following instructions on the [TzLibre website](https://tzlibre.github.io/whitelist.html#send-tx). + +- - - + +## Tech details + +This section explains technical details of the tools used in the sandboxed environment. It'll help you understand what happens under the hood: although the sendboxed environment enhances usability and security (and we encourage you to use it), you might choose to separately run each tool to produce the required signatures. + +### `pykeychecker` + +The `pykeychecker` is a fork of DLS [`fundraiser-tools`](https://github.com/tezos/fundraiser-tools) which fixes a broken dependency in the DLS repo. It also upgrades it to sign your Ethereum address and the declaration reported above. You can check the [code diff](https://github.com/tezos/fundraiser-tools/compare/master...tzlibre:master?diff=split&name=master]#diff-e6c8e5e03826917a611ce5c5e23626fc)). Note that this is the only code that interacts with private and sensitive data. + +#### Install system dependencies + +- [Python 2.x](https://www.python.org/) (it is installed by default on many systems) +- [libsodium](libsodium.org) (on OSX via brew: `brew install libsodium`, on Ubuntu: `sudo apt install libsodium-dev`) + +#### Install package dependencies + +```sh +cd pykeychecker; pip install -r requirements.txt; cd .. +``` +#### Run `pykeychecker` and save the output in a file named `keychecker_output.txt` + +```sh +python pykeychecker/keychecker.py <15_words_mnemonic_seed> <0x...yourEthAddress> > keychecker_output.txt +``` +> If your password contains special characters (e.g. `&`, `\`, `|`, `!`, etc.), encapsulate it with `'`. +> If your password contains `'`, prepend `'\'` (e.g.`I'mapassword -> 'I'\''mapassword'`). + +Here is a usage example: + +```sh +python pykeychecker/keychecker.py word1 word2 word word4 word5 word6 word7 word8 word9 word10 word11 word12 word13 word14 word15 your@email.com yourPassord 0x1234567890123456789012345678 +``` + +#### Verify the computed Tezos address + +Open `keychecker_output.txt` and verify that the computed Tezos address is correct. + +```sh +cat keychecker_output.txt | grep TZL_addr +``` + +If it does not match your Tezos address, please check input parameters again. + +You can verify that no private data is contained in this file. + +### `jstxencoder` + +`jstxencoder` serializes the output of `pykeychecker` to allow invoking an Ethereum smart contract method by issuing a standard tx with data. + +#### Install system dependencies + +- Node.js: use [nvm](https://github.com/creationix/nvm) to install the latest stable release. +- `make` and a proper C/C++ compiler toolchain (`sudo apt install build-essentials` on Ubuntu, Xcode on Mac OS X) + +#### Install package dependencies + +```sh +cd jstxencoder; npm install; cd .. +``` + +#### Run + +```sh +cat keychecker_output.txt | node jstxencoder +``` + +### `gen-signatures.sh` + +Instead of running `pykeychecker` and `jstxencoder` separately, we suggest to use the `gen-signature.sh` script to improve usability and security. + +To do so, issue the command and follow the provided instructions: + +```sh +bash gen-signatures.sh +``` + +Here is an example: + +```sh +$ bash gen-signature.sh + + ********* + * INPUT * + ********* + +15 words secret key: +word1 word2 word word4 word5 word6 word7 word8 word9 word10 word11 word12 word13 word14 word15 + +Email: +your@email.com + +Password: +yourPassword + +Ethereum address to whitelist: +0x1234567890123456789012345678 + + + ********** + * OUTPUT * + ********** + +Tezos address (your public key hash): +tz1bG4r2PkzsxARQFnScVr51NaXrcVdF1Myg + +Tezos public key (`TZL_pk`): +012f1e1c8ed3eea205c75323c6db7f2a74b3273921c06a6629056331612d275e + +Signature of the Ethereum address (`ETH_addrSignature`): +1a9871ca4357ef82ab8b427e428e1f430c78755f4d15b826d89bfc57e0309e3f468b5ea5b73921138f9a5e3d132a995c7bacd5a8a50800589e29232382e66c0d + +Signature of the declaration (`declarationSignature`): +460323168a9b29629586ea888be344243a12003c381a9c427d2ebd94406f5e0376f1240c7d97b9acd686fa6003e10d72bdffc3f7ddeb3c7904d783a392ca490f + +Transaction data: +0x1d997f8b012f1e1c8ed3eea205c75323c6db7f2a74b3273921c06a6629056331612d275e000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000401a9871ca4357ef82ab8b427e428e1f430c78755f4d15b826d89bfc57e0309e3f468b5ea5b73921138f9a5e3d132a995c7bacd5a8a50800589e29232382e66c0d0000000000000000000000000000000000000000000000000000000000000040460323168a9b29629586ea888be344243a12003c381a9c427d2ebd94406f5e0376f1240c7d97b9acd686fa6003e10d72bdffc3f7ddeb3c7904d783a392ca490f +``` + +We encourage you to read `gen-signature.sh` content to verify its behavior. diff --git a/gen-signatures.sh b/gen-signatures.sh new file mode 100644 index 0000000..3c5f0b8 --- /dev/null +++ b/gen-signatures.sh @@ -0,0 +1,39 @@ +#! /bin/bash +OUTPUT=keychecker_output.txt + +echo -e "\n *********" +echo -e " * INPUT *" +echo " *********" + +echo -e "\n15 words secret key:" +read MNEMONIC + +echo -e "\nEmail:" +read EMAIL + +echo -e "\nPassword:" +read PASSWORD + +echo -e "\nEthereum address to whitelist:" +read ETH_ADDR + +python pykeychecker/keychecker.py $MNEMONIC $EMAIL "$PASSWORD" $ETH_ADDR > $OUTPUT + +echo -e "\n\n **********" +echo -e " * OUTPUT *" +echo " **********" + +echo -e "\nTezos address (your public key hash):" +cat $OUTPUT | grep TZL_addr | cut -d ' ' -f2 + +echo -e "\nTezos public key (\`TZL_pk\`):" +cat $OUTPUT | grep TZL_pk | cut -d ' ' -f2 + +echo -e "\nSignature of the Ethereum address (\`ETH_addrSignature\`):" +cat $OUTPUT | grep ETH_addrSignature | cut -d ' ' -f2 + +echo -e "\nSignature of the declaration (\`declarationSignature\`):" +cat $OUTPUT | grep declarationSignature | cut -d ' ' -f2 + +echo -e "\nTransaction data:" +cat $OUTPUT | node jstxencoder diff --git a/jstxencoder/index.js b/jstxencoder/index.js new file mode 100644 index 0000000..933c335 --- /dev/null +++ b/jstxencoder/index.js @@ -0,0 +1,65 @@ +const fs = require('fs') +const coder = require('web3-eth-abi') + +const METHOD_SIGNATURE = 'whitelist(bytes32,bytes,bytes)' +const METHOD_INPUT_TYPES = ['bytes32', 'bytes', 'bytes'] + +/* LIBS */ + +function remove_0x (addr) { + return addr.replace('0x', '') +} + +function add_0x (addr) { + if (addr.startsWith('0x')) { + return addr + } + + return '0x' + addr +} + +function generate_tx_data (TZL_pk, ETH_addr_signature, declaration_signature) { + let params = Array.prototype.slice.call(arguments, 0).map(add_0x) + let encodedFunctionSignature = coder.encodeFunctionSignature(METHOD_SIGNATURE) + let encodedParameters = remove_0x(coder.encodeParameters(METHOD_INPUT_TYPES, params)) + let tx_data = encodedFunctionSignature + encodedParameters + + return remove_0x(tx_data) +} + +function parse_input () { + let stdinBuffer = fs.readFileSync(0) + let data = stdinBuffer.toString() + let parsed = JSON.parse(`{ + ${data.trim().split('\n') + .map(l => l.split(':') + .map(e => `"${e.trim()}"`) + .join(':') + ) + .join(',')} + }`) + + return parsed +} + +/** MAIN **/ + +function main () { + // parse input + let input = parse_input() + + // get tx data + let tx_data = generate_tx_data( + input.TZL_pk, + input.ETH_addrSignature, + input.declarationSignature + ) + + // Print transaction data + console.log(add_0x(tx_data)) + +} + +if (!module.parent) { + main() +} diff --git a/jstxencoder/package.json b/jstxencoder/package.json new file mode 100644 index 0000000..dc54bed --- /dev/null +++ b/jstxencoder/package.json @@ -0,0 +1,9 @@ +{ + "name": "jstxencoder", + "version": "1.0.0", + "main": "index.js", + "author": "TzLibre", + "dependencies": { + "web3-eth-abi": "^1.0.0-beta.30" + } +} diff --git a/pykeychecker/keychecker.py b/pykeychecker/keychecker.py index fd31c15..2023fd1 100755 --- a/pykeychecker/keychecker.py +++ b/pykeychecker/keychecker.py @@ -30,11 +30,12 @@ def ethdata_to_tz1(ethdata): if __name__ == '__main__': - if len(sys.argv) == 18: + if len(sys.argv) == 19: mnemonic = ' '.join(sys.argv[1:16]).lower() email = sys.argv[16] password = sys.argv[17] + eth_address = sys.argv[18].replace('0x','') salt = unicodedata.normalize( "NFKD", (email + password).decode("utf8")).encode("utf8") try: @@ -45,8 +46,15 @@ def ethdata_to_tz1(ethdata): pk, sk = pysodium.crypto_sign_seed_keypair(seed[0:32]) pkh = blake2b(pk,20).digest() - print "public key hash: ", tezos_pkh(pkh) - + msgHash = blake2b(eth_address.decode('hex'),64).digest() + sig = pysodium.crypto_sign(msgHash, sk)[:-len(msgHash)] + declarationHash = blake2b('I hereby cryptographically prove to be a contributor of Tezos Stiftung (CHE-290.597.458), a Swiss Foundation based in Gubelstrasse 11, 6300 Zug, Switzerland. I recognize and welcome the existence multiple implementations of Tezos. I ask and expect Tezos Stiftung to foster competition among them by funding and supporting their development, marketing and growth. Funds allotted to various Tezos implementations shall always be directly proportional to their market capitalization at the time of each distribution of funds. Distribution of funds to multiple existing Tezos implementations shall begin no later than January 1st 2019 and consistently continue throughout time. Following priorities autonomously set by each community, Tezos Stiftung shall distribute funds in the most appropriate, effective and transparent way.',64).digest() + declarationSig = pysodium.crypto_sign(declarationHash, sk)[:-len(declarationHash)] + print "TZL_addr:", tezos_pkh(pkh) + print "TZL_pk:", pk.encode('hex') + print "ETH_addrSignature:", sig.encode('hex') + print "declarationSignature:", declarationSig.encode('hex') + elif len(sys.argv) == 2: tz_input = sys.argv[1] assert(tz_input == bitcoin.bin_to_b58check(bitcoin.b58check_to_bin(tz_input)[2:], magicbyte=434591)) @@ -58,7 +66,7 @@ def ethdata_to_tz1(ethdata): else: print("""Usage: -python keychecker.py garage absurd steak ... email password +python keychecker.py or python keychecker.py tz1YoUrPuBlicKeYhaSh""") exit(1) diff --git a/pykeychecker/requirements.txt b/pykeychecker/requirements.txt index 0575d5d..d3da7af 100644 --- a/pykeychecker/requirements.txt +++ b/pykeychecker/requirements.txt @@ -1,3 +1,3 @@ --e git+https://github.com/vbuterin/pybitcointools.git#egg=bitcoin +-e git+https://github.com/vbuterin/pybitcointools.git@aeb0a2bbb8bbfe421432d776c649650eaeb882a5#egg=master pyblake2==0.9.3 pysodium==0.6.11