Lets start with whole numbers.

We will use lower-case letters for numbers. Here is a number:

a = 42

We will also use points on some elliptic curve.

Points are simply pairs of very large numbers that satisfy an elliptic curve equation.

We will use upper-case letters for points. Here is a point:

A = (4, 68)

Elliptic curves allow special kind of arithmetic on points. Two points can be “added” to produce another, seemingly random, point on the elliptic curve:

C = A + B

Point can be added to itself several times:

D = C + C + C

When one point is added many times we will say it is “multiplied by a number”:

D = 3·C

Turns out, if you add point A a lot of times (in other words, multiply it by a large enough number) and get another point B, it will be hard to figure out what that number was, provided you are given only the original point A and the resulting point B.

“Hard” means that to find out that number of additions, you cannot simply “divide” B by A, but have to enumerate a lot of possible numbers x to check if x·A produces B. So if x is very large, larger than number of atoms in the universe, checking all possibilities will take too long to bother. At the same time, if one knows correct x, computing x·A is pretty fast. We will use this asymmetry heavily in the discussion below.

Now we have enough building blocks to play a game.

Our players will be Alice (the prover) and Bob (the verifier).

Alice will need to prove that she knows some secret number x in a way that Bob does not learn that number.

Bob will need to verify that Alice indeed knows the number x and did not cheat.

Before we start, we need a base point on the elliptic curve. It does not matter which point exactly, only that it’s a common point for both players.

B := some common point

Then, Alice chooses a number x in a way that’s unpredictable to the verifier:

x := random number

Alice converts her secret number to a point by adding the base point B to itself x times and producing new point X:

X := x·B

Alice sends point X to Bob and they can start the game. Bob knows points B and X, but due to asymmetry of point arithmetic, cannot find out number x efficiently.

Bob now asks Alice to prove the knowledge of such x that X is a result of adding B to itself x times.

Lets start with a naïve approach: what if Alice simply sends number x to Bob? Bob then can compute x·B and check that it equals X received earlier. The proof succeeds, but now one more person knows the secret x which is not very useful.

To improve the scheme we will blind number x so that verifier does not learn it. Alice will choose an additional number r, equally unpredictable to Bob:

r := random number

Then, Alice will add numbers x and r together:

s := x + r

Note that r should be secret as well, otherwise x can be calculated from s and r by a simple subtraction.

Finally, Alice converts r to a point, just like she converted x earlier, by adding base point B to itself r times:

R := r·B

Alice sends the number s and point R to Bob.

The number s here represents a blinded secret, or, in other words, “secret x hidden in some noise r”. Point R is used to tell Bob enough about the noise to verify the proof, but not reveal the actual amount of noise.

Bob adds X (received before the start of the game) and R together:

S := X + R

Then, Bob converts s to the point:

S’ := s·B

And, finally, checks that S equals S’.

This scheme protects the secrecy of x because from number s and points X and R it is very hard to extract underlying numbers x and r.

Unfortunately, this scheme allows Alice to cheat.

Alice knows that Bob will be adding point X to her temporary point R and then asking for an underlying number s. If Alice does not actually know number x, she can subtract point X from the point R:

R := r·B – X

When Bob adds R and X together, X will get cancelled out and only r·B will remain:

S := X + R

S := X + r·B – X

S := r·B

Alice then sets the blinded number s to r without any knowledge of x and satisfies Bob’s check that s·B == R + X.

To prevent Alice from cancelling out X, Bob will use a trick. Instead of adding X to R one time, Bob will add X many times. We will call this number e:

S := e·X + R

What is more important is that Bob will choose the number e only after Alice sends her point R.

Previously Alice was choosing random numbers (x and r), but now it is Bob’s turn.

The number e will not be secret all the time, but only when Alice chooses number r and sends point R.

e must be unpredictable enough to Alice so that she cannot preemptively cancel out e·X from R like she cancelled out X in the previous example.

So the updated scheme looks like this:

Alice chooses random number r and sends point r·B to Bob.

Bob chooses random number e and sends it to Alice.

Alice knows that Bob will add X to R e times, so she has to add x the same number of times:

s := e·x + r

The original secret x is blinded by temporary secret r as usual, but it is also multiplied by number e the sole purpose of which is to prevent Alice cancelling out X from R.

Alice sends s to Bob.

Bob checks that s is really an underlying number for a composite point made of R and X:

s·B == R + e·X

Bob now is certain that Alice could not have come up with number s other than by knowing both numbers r and x, because X and R were fixed before e was given to Alice.

Notice that up until introduction of anti-cheating device e, Bob was not sending anything back to Alice. The protocol became interactive only when Bob required Alice to commit to point R (that contains noise) before telling her a random number e.

As a result, instead of one move (Alice sending some proof to Bob), the protocol became a three-move: Alice sends Bob R, Bob challenges Alice with e, Alice sends back final proof s.

Because of that three-move that looks like a greek letter sigma (∑), the protocol is called “sigma protocol”.

To make it more practical and suitable for digital signatures, we should make it non-interactive.

Alice should be able to produce a proof that can be checked by Bob or anyone else without interacting with Alice again, but maintaining the three-move structure that prevents cheating, yet protects secret numbers.

As it turns out, we can use a hash function to make sure e is not predictable until R is specified. If Alice uses a hash function deemed secure by Bob, then Bob can be perfectly satisfied with e computed pseudo-randomly as follows:

e = Hash( R )

If Alice uses such e, then she will not be able to use it to cancel out e·X from R. She would have to try many possible numbers e, compute many possible R := r·B – e·X, until hash function returns e in output for that R.

If hash function returns large enough numbers and mixes input data well, then such process would take an impossibly long time.

Which means, Alice will not be able to cancel out e·X from her noise commitment R and will be forced to calculate number s correctly using both secrets x and r:

s := e·x + r

The full protocol would look like this:

Alice chooses random r, computes R := r·B, uses hash function to get a random “challenge” e := Hash( R ), and computes s := e·x + r.

Alice sends point R and number s to Bob who verifies that s·B equals R + e·X. In fact, not only Bob, but anyone else in the world can independently verify that proof.

Finally, to make a signature out of the proof, Alice needs to customize hash function with a message that she is signing. This is needed to make sure that a signature for one message cannot be reused for another message.

This customization is typically done by providing not only R to the hash function, but also the message itself:

e := Hash(R, message)

A good hash function would return a different output if even one bit of the message is changed, making already computed s invalid. This is because a different e means that X must be added to R different number of times, therefore number s must be adjusted by the same number of times of secret x. And only the knowledge of x allows doing that, which is equivalent to legitimately signing a different message.

And this is how Schnorr signatures work. E.g. EdDSA standard described in RFC8032.

PS. A compact outline of the protocol: