7 minute read


Signature verification is a foundational security concept that is used to verify the origin and integrity of data. Combined with zkSNARKs, it enables novel opportunities to enrich your (decentralized) application. In this post, you will learn

  1. How to create an Eddsa signature with Znakes.
  2. Prove and verify the correctness of a signature with ZoKrates.

Remarks

At the ZoKrates Documentation you can find further information about the language, the standard library, supported proof backends, and much more. Make sure you have followed the instructions in the Getting Started chapter and are able to run the “Hello World” example.
All the material used in this tutorial can be found in our tutorials repository. Please note that all the source code displayed on this page was compiled using ZoKrates 0.8.6. We do not guarantee its compatibility with future versions.

Introduction

Signatures are a well-established cryptographic mechanism that, in combination with zkSNARKs, become a powerful mechanism. A signature is a digital equivalent of a handwritten signature used to authenticate the integrity and authenticity of a message, document, or transaction. It enables the recipient to verify the origin and integrity of the data.

Signatures are based on asymmetric cryptography, where you have two keys: a private and a public key. A signature is created (encrypted) using a private key and verified (decrypted) using the corresponding public key. Common signature schemes include RSA, ECDSA (Elliptic Curve Digital Signature Algorithm), and EdDSA (Edwards-curve Digital Signature Algorithm).

The below sceme shows how signatures are used in digital signing of messages. Additionally, in step 4 the sender encrypts both the original message and the digital signature using the recipient’s public key. And in step 6 the receiver uses its private key to decrypt the contents of the digital envelope. This ensures confidentiality, so the message is only readable by the person for whom it is intended.

Digital signing scheme [1]

Application

Signatures find application in a variety of contexts, for example:

  • Auditability and compliance: By leveraging zkSNARKs, one can prove compliance with specific regulations or policies without revealing sensitive information. The addition of signatures ensures the accountability and integrity of the proof. This is further described in the Verifiable Credential course and in this paper.

  • Privacy: By using zkSNARKs, one can prove the correctness of a computation without revealing the details of the computation itself. When combined with signatures, this allows for privacy-preserving transactions or data exchanges, where the origin and integrity of the data can be verified without revealing any sensitive information. This is done for example in the cryptocurrency Zcash.

  • Accountability: Signatures provide accountability by associating a specific action or transaction with a known entity. By combining signatures with zkSNARKs, it becomes possible to prove that a specific action was performed correctly by a particular entity without revealing any additional information about the underlying computation or data.

  • Scalability: zkSNARKs enable succinct and efficient proofs, reducing the computational and storage requirements compared to more traditional zero-knowledge proof systems. By combining zkSNARKs with signatures, the resulting system can provide scalability benefits, making it more practical for real-world applications where efficiency is crucial. An application of this is shown in this tutorial about scaling transactions on the Tezos blockchain using zk-rollups.

Tutorial: Verifying an Eddsa signature

We’ll implement an operation that’s very typical in blockchain use-cases: Proving the correct verification of an Eddsa signature. In particular, we’ll show how ZoKrates and the command line can be used to allow a prover, let’s call her Peggy, to demonstrate beyond any reasonable doubt to a verifier, let’s call him Victor, that the message that Peggy send to Victor was really created by her.

1. Creating an Eddsa Signature with Znakes

We will start this tutorial by using Znakes to create an Eddsa signature for an arbitrarily chosen secret message.

Notice: Besides the BN254 elliptic-curve used e.g. by Ethereum and in this Tutorial, Znakes also supports the BLS12_381 elliptic curve which e.g. the Tezos blockchain uses. For more information and how to set up Znakes visit the Znakes repo.

So let’s dive in!

First, we (Peggy) create a new file named create-signature.py with the following content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import hashlib

from znakes.curves import BabyJubJub
from znakes.eddsa import PrivateKey, PublicKey
from znakes.utils import write_signature_for_zokrates_cli

if __name__ == "__main__":

    raw_msg = "This is my secret message"
    msg = hashlib.sha512(raw_msg.encode("utf-8")).digest()

    sk = PrivateKey.from_rand(curve=BabyJubJub);
    sig = sk.sign(msg)

    pk = PublicKey.from_private(sk)
    is_verified = pk.verify(sig, msg)
    print(is_verified)

    path = 'zokrates_inputs.txt'
    write_signature_for_zokrates_cli(pk, sig, msg, path)

Like in the signature-sceme shown in the introduction, the secret message is first hashed (here with sha512 hashfunction) and turned into a digest. We then create a private key and create the signature by signing the digest with this private key. Next, we create a public key (representing Peggy) from our private key. With the public key we then verify for demonstration purposes whether the signature and our digest belong together. Finally, we write the input arguments for verifyEddsa() in the ZoKrates stdlib to a file called zokrates_inputs.txt.

We can now can run this python script via:

python create-signature.py

This creates a file called zokrates_inputs.txt. The content should look from the structure like the following:

10041775272610680597649138558111867140088287599035431170728241228669634925671 19045584355489137154300255038437027652180257880634202059955435891798466344432 14517916597883362893064608394843629693674165114908520112595055382047085957383 14897476871502190904409029696666322856887678969656209656241038339251270171395 16668832459046858928951622951481252834155254151733002984053501254009901876174 3814687126 4207057211 2301474087 1696421512 1054042432 4114589074 2402006685 2358319779 2636307903 771130895 3338794104 910337493 3941248527 2566242658 3403499691 2178970740

These inputs can then be used to create a witness with the ZoKrates CLI. But before we (Peggy) can create a proof with ZoKrates, we need to specify a ZoKrates program.

2.1 Asserting an Eddsa signature with ZoKrates

Now that Peggy (prover) has created a signature containing a secret message, Victor (verifier) wants to verify whether it was actually Peggy who created this message and nobody else. We can now create a small ZoKrates program which wraps the existing verifyEddsa function in the standard library. For this we create a file named verify_signature.zok with the following content:

from "ecc/babyjubjubParams" import BabyJubJubParams;
import "signatures/verifyEddsa.code" as verifyEddsa;
import "ecc/babyjubjubParams.code" as context;

def main(private field[2] R, private field S, field[2] A, u32[8] M0, u32[8] M1) -> (bool) {
  // context necessary for the signature verification
  BabyJubJubParams context = context();
  //Verify Signature (R,S) with PupKey (A) on Hash (M0, M1);
  bool isVerified = verifyEddsa(R, S, A, M0, M1, context);
  
  return isVerified;
}  

We first import babyJubJubParams and babyjubjubParams.code which together specify the elliptic curve functionality for the public/private keys (compatible with e.g. the Ethereum blockchain). We also import the verifyEddsa function from the standard library.

As private input we have the signature made out of R and S. As public input we first have the public key (A) of some entity or authority (here Peggy). At last, the secret message is given as a hash-digest consisting of M0 and M1.

Next, we define the context() which is necessary for the signature verification. At last, we verify the signature by calling verifyEddsa() with all our input paramters and the context.

2.2 Verifying correctness of an Eddsa signature with the ZoKrates CLI

Having described the problem in ZoKrates’ DSL, Victor can now continue using the ZoKrates CLI for the rest of the workflow.

First, we compile the program into an arithmetic circuit using the compile command.

zokrates compile -i verifySignature.zok

Next, we create a witness file using the following command:

zokrates compute-witness -a 10041775272610680597649138558111867140088287599035431170728241228669634925671 19045584355489137154300255038437027652180257880634202059955435891798466344432 14517916597883362893064608394843629693674165114908520112595055382047085957383 14897476871502190904409029696666322856887678969656209656241038339251270171395 16668832459046858928951622951481252834155254151733002984053501254009901876174 3814687126 4207057211 2301474087 1696421512 1054042432 4114589074 2402006685 2358319779 2636307903 771130895 3338794104 910337493 3941248527 2566242658 3403499691 2178970740

Using the flag -a we pass arguments to the program.

Still here? Great! Now all thats left is to generate the proof and verify the proof.

zokrates generate-proof
zokrates verify

Below you can see all the commands put together:

zokrates compile -i verifySignature.zok
zokrates setup
zokrates compute-witness -a zokrates compute-witness -a 10041775272610680597649138558111867140088287599035431170728241228669634925671 19045584355489137154300255038437027652180257880634202059955435891798466344432 14517916597883362893064608394843629693674165114908520112595055382047085957383 14897476871502190904409029696666322856887678969656209656241038339251270171395 16668832459046858928951622951481252834155254151733002984053501254009901876174 3814687126 4207057211 2301474087 1696421512 1054042432 4114589074 2402006685 2358319779 2636307903 771130895 3338794104 910337493 3941248527 2566242658 3403499691 21789707402454965239 1171055932
zokrates generate-proof
zokrates verify

output: PASSED

If the output says “PASSED”, the signature was successfully verified. This means that the secret message was actually send by the owner of the public/private key pair, in our case Peggy. And that the content was not changed (integrity).

Conclusion

At this point, you have successfully created a valid Eddsa signature with Znakes and verfied the Eddsa signature using ZoKrates. Or in other words, Peggy has proven that she was the actual creator of the message received by Victor. Congratulations!

Sources

[1] Küpper, Axel. (2021). Electronic Commerce: Module at the TU-Berlin (SNET).

Leave a comment