Commit fa18ef8e authored by Dario Pinto's avatar Dario Pinto
Browse files

Merge branch 'master' into 'master'

add article update urls

See merge request OCamlPro/www!67
parents d062ecbf e017e792
title=Signing Data for Smart Contracts
authors=Çagdas Bozman
Smart contracts calls already provide a built-in authentication mechanism as transactions (i.e. call operations) are cryptographically signed by the sender of the transaction. This is a guarantee on which programs can rely.
However, sometimes you may want more involved or flexible authentication schemes. The ones that rely on signature validity checking can be implemented in Michelson, and Liquidity provide a built-in instruction to do so. (You still need to keep in mind that you cannot store unencrypted confidential information on the blockchain).
This instruction is `Crypto.check` in Liquidity. Its type can be written as:
Crypto.check: key -> signature -> bytes -> bool
Which means that it takes as arguments a public key, a signature and a sequence of bytes and returns a Boolean. `Crypto.check pub_key signature message` is `true` if and only if the signature `signature` was obtained by signing the Blake2b hash of `message` using the private key corresponding to the public key `pub_key`.
A small smart contract snippet which implements a signature check (against a predefined public key kept in the smart contract's storage) [can be tested online here.](
type storage = key
let%entry main ((message : string), (signature : signature)) key =
let bytes = Bytes.pack message in
if not (Crypto.check key signature bytes) then
failwith "Wrong signature";
([] : operation list), key
This smart contract fails if the string `message` was not signed with the private key corresponding to the public key `key` stored. Otherwise it does nothing.
This signature scheme is more flexible than the default transaction/sender one, however it requires that the signature can be built outside of the smart contract. (And more generally outside of the toolset provided by Liquidity and Tezos). On the other hand, signing a transaction is something you get for free if you use the tezos client or any tezos wallet (as is it essentially their base function).
The rest of this blog post will focus on various ways to sign data, and on getting signatures that can be used in Tezos and Liquidity directly.
### Signing Using the Tezos Client
One (straightforward) way to sign data is to use the Tezos client directly. You will need to be connected to a Tezos node though as the client makes RPCs to serialize data (this operation is protocol dependent). We can only sign sequences of bytes, so the first thing we need to do is to serialize whichever data we want to sign. This can be done with the command `hash data` of the client.
$ ./tezos-client -A -P 80 hash data '"message"' of type string
Raw packed data:
Raw Blake2b hash:
Raw Sha256 hash:
Raw Sha512 hash:
Gas remaining: 399918 units remaining
This command gives the result of hashing the data using various algorithms but what we're really interested in is the first item `Raw packed data` which is the serialized version of our data (`"message"`) : `0x0501000000076d657373616765`.
We can now sign these bytes using the Tezos client as well. This step can be performed completely offline, for that we need to use the option `-p` of the client to specify the protocol we want to use (the `sign bytes` command will not be available without first selecting a valid protocol). Here we use protocol 3, designated by its hash `PsddFKi3`.
$ ./tezos-client -p PsddFKi3 sign bytes 0x0501000000076d657373616765 for my_account
The account `my_account` can be any imported account in the Tezos client. In particular, it can be an encrypted key pair (you will need to enter a password to sign) or a hardware Ledger (you will need to confirm the signature on the Ledger). The obtained signature can be used as is with Liquidity or Michelson. This one starts with `edsig` because it was obtained using an Ed25519 private key, but you can also get signatures starting with `spsig1` or `p2sig` depending on the cryptographic curve that you use.
### Signing Manually
In this second section we detail the necessary steps and provide a Python script to sign string messages using an Ed25519 private key. This can be easily adapted for other signing schemes.
These are the steps that will need to be performed in order to sign a string:
- Assuming that the value you want to sign is a string, you first need to convert its ASCII version to hexa, for the string `"message"` that is `6d657373616765`.
- You need to produce the packed version of the corresponding Michelson expression. The binary representation can vary depending on the types of the values you want to pack but for strings it is:
| 0x | 0501 | [size of the string on 4 bytes] | [ascii string in hexa] |
for `"message"` (of length 7), it is
| 0x | 0501 | 00000007 | 6d657373616765 |
or `0x0501000000076d657373616765`.
- Hash this value using [Blake2b]( (`01978930fd2d04d0db8c2e4ef8a3f5d63b8e732177c8723135ed0dc7d99ebed3`) which is 32 bytes long.
- Depending on your public key, you then need to sign it with the corresponding curve (ed25519 for edpk keys), the signature is 64 bytes:
- Optionally convert to base58check. This is not needed because Liquidity and Michelson allow signatures (as well as keys and key hashes) to be given in hex format with a 0x:
The following Python (3) script will do exactly this, entirely offline. Note that this is just an toy example, and should not be used in production. In particular you need to give your private key on the command line so this might not be secure if the machine you run this on is not secure.
$ pip3 install base58check pyblake2 ed25519
> python3 ./ "message" edsk2gL9deG8idefWJJWNNtKXeszWR4FrEdNFM5622t1PkzH66oH3r
#### ``
from pyblake2 import blake2b
import base58check
import ed25519
import sys
message = sys.argv[1]
seed_b58 = sys.argv[2]
prefix = b'\x05\x01'
len_bytes = (len(message)).to_bytes(4, byteorder='big')
h = blake2b(digest_size=32)
b = bytearray()
h.update(prefix + len_bytes + b)
digest = h.digest()
seed = base58check.b58decode(seed_b58)[4:-4]
sk = ed25519.SigningKey(seed)
sig = sk.sign(digest)
print("0x" + sig.hex())
......@@ -8,8 +8,6 @@ let old_to_new =
; ("/fr/recrutement-ocamlpro/", "/jobs")
; ( ""
, "/" )
; ( ""
, "/" )
; ( ""
, "/" )
; ( ""
......@@ -218,6 +216,8 @@ let old_to_new =
, "/blog/2019_02_11_whats_new_for_alt_ergo_in_2018_here_is_a_recap" )
; ( "/2019/03/05/techelson-a-test-execution-engine-for-michelson/"
, "/blog/2019_03_05_techelson_a_test_execution_engine_for_michelson" )
; ( "/2019/03/05/signing-data-for-smart-contracts/"
, "/blog/2019_03_05_signing_data_for_smart_contracts" )
; ("/2019/04/10/opam-2-0-4-release/", "/blog/2019_04_10_opam_2.0.4_release")
; ( "/2019/04/29/blockchains-at-ocamlpro-an-overview/"
, "/blog/2019_04_29_blockchains_at_ocamlpro_an_overview" )
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment