Skip to content

Instantly share code, notes, and snippets.

@Zenithar
Last active September 16, 2023 17:46
Show Gist options
  • Save Zenithar/4d891a3bef9b0dc46358b55ea9e6bd3d to your computer and use it in GitHub Desktop.
Save Zenithar/4d891a3bef9b0dc46358b55ea9e6bd3d to your computer and use it in GitHub Desktop.

Revisions

  1. Zenithar revised this gist Sep 16, 2023. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions ~readme.md
    Original file line number Diff line number Diff line change
    @@ -53,4 +53,5 @@ Interactive protocol to authenticate a secret knowledge without sharing it on th
    00000010 f8 0d db 9f 86 36 30 40 59 ab c2 15 fb d6 02 08 |.....60@Y.......|
    {"time":"2023-09-16T19:23:25.66588808+02:00","level":"INFO","msg":"S: Client Authenticated?"}
    true
    ```
  2. Zenithar revised this gist Sep 16, 2023. No changes.
  3. Zenithar revised this gist Sep 16, 2023. 1 changed file with 15 additions and 3 deletions.
    18 changes: 15 additions & 3 deletions zkpauth.go
    Original file line number Diff line number Diff line change
    @@ -36,6 +36,7 @@ func (p *Server) Bootstrap() ([]byte, error) {
    return nil, fmt.Errorf("unable to generate ephemeral key pair: %w", err)
    }

    // G2a || SIGN(a2, G2a) || G3a || SIGN(a3, G3a)
    var out []byte
    out = append(out, p.g2a...)
    out = append(out, ed25519.Sign(p.a2, p.g2a)...)
    @@ -47,7 +48,7 @@ func (p *Server) Bootstrap() ([]byte, error) {

    func (p *Server) Prove(proof, secret []byte) ([]byte, error) {
    // Ensure expected length
    // g2b || g2b signature || g3b || g3b signature || Pb || Qb
    // G2b || G2b signature || G3b || G3b signature || Pb || Qb
    if len(proof) < 2*(ed25519.PublicKeySize+ed25519.SignatureSize)+2*ed25519.PublicKeySize {
    return nil, errors.New("message too short")
    }
    @@ -106,7 +107,7 @@ func (p *Server) Prove(proof, secret []byte) ([]byte, error) {
    return nil, fmt.Errorf("unable to prepare secret as scalar: %w", err)
    }

    //
    // Convert to point for arithmetic
    p.qB, err = (&edwards25519.Point{}).SetBytes(qb)
    if err != nil {
    return nil, fmt.Errorf("unable to prepare secret public key as point: %w", err)
    @@ -116,8 +117,11 @@ func (p *Server) Prove(proof, secret []byte) ([]byte, error) {
    return nil, fmt.Errorf("unable to prepare secret public key as point: %w", err)
    }

    // Pa = s * G3
    p.pA = (&edwards25519.Point{}).ScalarMult(s, G3)
    // Qa = x * G2 + s * G
    p.qA = (&edwards25519.Point{}).VarTimeDoubleScalarBaseMult(x, G2, s)
    // Ra = a3 * (Qa - Qb)
    Ra := (&edwards25519.Point{}).ScalarMult(a3, (&edwards25519.Point{}).Subtract(p.qA, p.qB))

    var out []byte
    @@ -141,8 +145,10 @@ func (p *Server) Verify(proof []byte) (bool, error) {
    }

    Rb, _ := (&edwards25519.Point{}).SetBytes(proof)
    // Rab = a3 * Rb
    Rab := (&edwards25519.Point{}).ScalarMult(a3, Rb)

    // CHECK( Rb == (Pa - Pb) )
    if (&edwards25519.Point{}).Subtract(p.pA, p.pB).Equal(Rab) != 1 {
    return false, errors.New("invalid proof")
    }
    @@ -188,6 +194,7 @@ func (v *Client) Proof(in, secret []byte) ([]byte, error) {
    return nil, fmt.Errorf("unable to generate ephemeral key pair: %w", err)
    }

    // Convert to scalars for arithmetric
    b2, err := privateToScalar(v.b2)
    if err != nil {
    return nil, fmt.Errorf("unable to initialize a scalar from ephemeral key: %w", err)
    @@ -208,14 +215,16 @@ func (v *Client) Proof(in, secret []byte) ([]byte, error) {
    }
    r, _ := edwards25519.NewScalar().SetBytesWithClamping(challenge[:])

    // Compute secret
    // Compute secret (use SH512/256)
    h := sha512.Sum512_256(secret)
    y, err := edwards25519.NewScalar().SetBytesWithClamping(h[:])
    if err != nil {
    return nil, fmt.Errorf("unable to prepare secret as scalar: %w", err)
    }

    // Pb = r * G3
    v.p = (&edwards25519.Point{}).ScalarMult(r, G3)
    // Qb = y * G2 + r * G
    v.q = (&edwards25519.Point{}).VarTimeDoubleScalarBaseMult(y, G2, r)

    var out []byte
    @@ -252,9 +261,12 @@ func (v *Client) Verify(proof []byte) ([]byte, error) {
    Qa, _ := (&edwards25519.Point{}).SetBytes(qa)
    Ra, _ := (&edwards25519.Point{}).SetBytes(ra)

    // Rb = b3 * (Qa - Qb)
    Rb := (&edwards25519.Point{}).ScalarMult(b3, (&edwards25519.Point{}).Subtract(Qa, v.q))
    // Rab = b3 * Ra
    Rab := (&edwards25519.Point{}).ScalarMult(b3, Ra)

    // CHECK( Rab == (Pa - Pb) )
    if (&edwards25519.Point{}).Subtract(Pa, v.p).Equal(Rab) != 1 {
    return nil, errors.New("invalid proof")
    }
  4. Zenithar revised this gist Sep 16, 2023. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions ~readme.md
    Original file line number Diff line number Diff line change
    @@ -4,6 +4,8 @@ Based on
    * https://www.cossacklabs.com/blog/introducing_secure_comparator/
    * https://www.cossacklabs.com/files/secure-comparator-paper-rev12.pdf

    Interactive protocol to authenticate a secret knowledge without sharing it on the wire.

    ```
    {"time":"2023-09-16T19:23:25.663348882+02:00","level":"INFO","msg":"C -> S: Session boostrap"}
    {"time":"2023-09-16T19:23:25.664432655+02:00","level":"INFO","msg":"S -> C: Session public keys"}
  5. Zenithar renamed this gist Sep 16, 2023. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  6. Zenithar revised this gist Sep 16, 2023. 1 changed file with 54 additions and 0 deletions.
    54 changes: 54 additions & 0 deletions _readme.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,54 @@
    # Zero Knowledge based authentication

    Based on
    * https://www.cossacklabs.com/blog/introducing_secure_comparator/
    * https://www.cossacklabs.com/files/secure-comparator-paper-rev12.pdf

    ```
    {"time":"2023-09-16T19:23:25.663348882+02:00","level":"INFO","msg":"C -> S: Session boostrap"}
    {"time":"2023-09-16T19:23:25.664432655+02:00","level":"INFO","msg":"S -> C: Session public keys"}
    00000000 69 c4 9d 53 f7 ac e9 13 5a 7b 1f af a9 80 5c 2a |i..S....Z{....\*|
    00000010 87 f4 94 e0 a3 5a 88 87 0a ac 86 d2 d5 ab de 48 |.....Z.........H|
    00000020 60 a4 b4 bb 99 30 34 e3 04 d7 31 38 7a 1b 68 2e |`....04...18z.h.|
    00000030 38 f6 c0 40 0e 6a 18 54 ad f7 41 83 27 d2 97 5e |[email protected].'..^|
    00000040 fe 03 19 6b 16 02 e8 a1 4a 5a 35 f6 80 35 08 a1 |...k....JZ5..5..|
    00000050 b3 4f fb 87 93 38 ac ca 15 a3 44 d3 7c 92 fc 09 |.O...8....D.|...|
    00000060 b7 b8 18 0e 53 e5 1f 61 30 0c bf 82 fb 0d 5d 20 |....S..a0.....] |
    00000070 b5 dd 8f 15 e4 fa 52 c1 15 b7 5c 62 39 6d 0a 8c |......R...\b9m..|
    00000080 ed df e3 b9 55 d6 a1 56 e7 18 2e a1 6c a5 bd 1c |....U..V....l...|
    00000090 cd cf ab 54 0b ce be 73 e5 eb 24 43 51 f8 66 d5 |...T...s..$CQ.f.|
    000000a0 e6 70 09 ce f4 31 54 47 1a e9 c4 c8 5d b8 92 33 |.p...1TG....]..3|
    000000b0 71 98 ad 3e 65 d6 26 da 6d f0 40 ed 0a 67 2a 09 |q..>e.&[email protected]*.|
    {"time":"2023-09-16T19:23:25.665291918+02:00","level":"INFO","msg":"C -> S: Client proof"}
    00000000 64 58 ad 62 91 af 5a 45 c4 db bb bb 7d 7a 7f e4 |dX.b..ZE....}z..|
    00000010 c9 f7 a9 ab 4d c7 62 83 09 24 cd 23 0a 93 16 74 |....M.b..$.#...t|
    00000020 38 98 ec 56 b9 fc 84 55 30 7f 25 de e6 d5 0d 1b |8..V...U0.%.....|
    00000030 28 65 bf b5 5b 06 ec 3b 5c 33 11 d8 f1 0c 4f 81 |(e..[..;\3....O.|
    00000040 ed 21 9f f2 76 37 ec 91 26 89 9b 67 d3 8a dc 7c |.!..v7..&..g...||
    00000050 b1 9b 88 5c e5 98 6f 1b b4 c8 1d ce f8 2a 45 0c |...\..o......*E.|
    00000060 e1 f1 56 2b 9a ea df 14 af a2 94 f0 5e 54 af 7a |..V+........^T.z|
    00000070 c3 56 f5 57 9e 0a 91 ef 24 79 be 02 7d 03 ec 24 |.V.W....$y..}..$|
    00000080 fd 25 95 c3 dc 75 7c 1f 66 02 a6 e1 20 6a 36 79 |.%...u|.f... j6y|
    00000090 f5 24 57 8c ac 2c a2 04 7f e0 50 89 28 33 91 42 |.$W..,....P.(3.B|
    000000a0 50 6e 4a 46 e5 06 d9 dd af 03 fc ea f8 8e 73 ec |PnJF..........s.|
    000000b0 6a 80 84 e2 6b 23 2f e8 ca e6 3d db bc 2b fb 07 |j...k#/...=..+..|
    000000c0 dd b4 6a 8a 50 79 c6 2b e1 dd 0c 9e d4 08 67 68 |..j.Py.+......gh|
    000000d0 ed a8 e2 70 cf e8 4d 22 fa a1 88 13 42 7e 64 32 |...p..M"....B~d2|
    000000e0 97 df 19 bf 15 ad a5 f3 de 05 14 93 7b 27 bd e7 |............{'..|
    000000f0 10 53 ca cf 80 79 37 95 ff df 24 5c 23 e1 85 03 |.S...y7...$\#...|
    {"time":"2023-09-16T19:23:25.665662329+02:00","level":"INFO","msg":"S -> C: Session proof"}
    00000000 ae 6b 6d 7f 0c c3 8b 74 42 9b fe cc 07 fa 33 88 |.km....tB.....3.|
    00000010 8b dc aa 66 90 1e 45 c7 f2 88 e0 c2 c8 bb a7 c7 |...f..E.........|
    00000020 a6 2f e8 bd 2a 4d 76 25 26 dc a3 ad 3e 4f f4 ab |./..*Mv%&...>O..|
    00000030 99 ff 0f 80 77 9d 3e bb 4d 03 79 73 d7 ed 66 57 |....w.>.M.ys..fW|
    00000040 44 40 ec fc ba 11 08 cd b3 a0 c1 a3 ec f0 20 d4 |D@............ .|
    00000050 1a 61 40 41 a7 b2 9e 88 38 6f e2 56 58 a9 2d 18 |[email protected].|
    {"time":"2023-09-16T19:23:25.665825205+02:00","level":"INFO","msg":"C -> S : Session proof validation"}
    00000000 e6 12 de a2 bd 1c bd 59 af bd c7 eb 80 27 b7 97 |.......Y.....'..|
    00000010 f8 0d db 9f 86 36 30 40 59 ab c2 15 fb d6 02 08 |.....60@Y.......|
    {"time":"2023-09-16T19:23:25.66588808+02:00","level":"INFO","msg":"S: Client Authenticated?"}
    ```
  7. Zenithar revised this gist Sep 16, 2023. 1 changed file with 0 additions and 54 deletions.
    54 changes: 0 additions & 54 deletions output.txt
    Original file line number Diff line number Diff line change
    @@ -1,54 +0,0 @@
    # Zero Knowledge based authentication

    Based on
    * https://www.cossacklabs.com/blog/introducing_secure_comparator/
    * https://www.cossacklabs.com/files/secure-comparator-paper-rev12.pdf

    ```
    {"time":"2023-09-16T19:23:25.663348882+02:00","level":"INFO","msg":"C -> S: Session boostrap"}
    {"time":"2023-09-16T19:23:25.664432655+02:00","level":"INFO","msg":"S -> C: Session public keys"}
    00000000 69 c4 9d 53 f7 ac e9 13 5a 7b 1f af a9 80 5c 2a |i..S....Z{....\*|
    00000010 87 f4 94 e0 a3 5a 88 87 0a ac 86 d2 d5 ab de 48 |.....Z.........H|
    00000020 60 a4 b4 bb 99 30 34 e3 04 d7 31 38 7a 1b 68 2e |`....04...18z.h.|
    00000030 38 f6 c0 40 0e 6a 18 54 ad f7 41 83 27 d2 97 5e |[email protected].'..^|
    00000040 fe 03 19 6b 16 02 e8 a1 4a 5a 35 f6 80 35 08 a1 |...k....JZ5..5..|
    00000050 b3 4f fb 87 93 38 ac ca 15 a3 44 d3 7c 92 fc 09 |.O...8....D.|...|
    00000060 b7 b8 18 0e 53 e5 1f 61 30 0c bf 82 fb 0d 5d 20 |....S..a0.....] |
    00000070 b5 dd 8f 15 e4 fa 52 c1 15 b7 5c 62 39 6d 0a 8c |......R...\b9m..|
    00000080 ed df e3 b9 55 d6 a1 56 e7 18 2e a1 6c a5 bd 1c |....U..V....l...|
    00000090 cd cf ab 54 0b ce be 73 e5 eb 24 43 51 f8 66 d5 |...T...s..$CQ.f.|
    000000a0 e6 70 09 ce f4 31 54 47 1a e9 c4 c8 5d b8 92 33 |.p...1TG....]..3|
    000000b0 71 98 ad 3e 65 d6 26 da 6d f0 40 ed 0a 67 2a 09 |q..>e.&[email protected]*.|

    {"time":"2023-09-16T19:23:25.665291918+02:00","level":"INFO","msg":"C -> S: Client proof"}
    00000000 64 58 ad 62 91 af 5a 45 c4 db bb bb 7d 7a 7f e4 |dX.b..ZE....}z..|
    00000010 c9 f7 a9 ab 4d c7 62 83 09 24 cd 23 0a 93 16 74 |....M.b..$.#...t|
    00000020 38 98 ec 56 b9 fc 84 55 30 7f 25 de e6 d5 0d 1b |8..V...U0.%.....|
    00000030 28 65 bf b5 5b 06 ec 3b 5c 33 11 d8 f1 0c 4f 81 |(e..[..;\3....O.|
    00000040 ed 21 9f f2 76 37 ec 91 26 89 9b 67 d3 8a dc 7c |.!..v7..&..g...||
    00000050 b1 9b 88 5c e5 98 6f 1b b4 c8 1d ce f8 2a 45 0c |...\..o......*E.|
    00000060 e1 f1 56 2b 9a ea df 14 af a2 94 f0 5e 54 af 7a |..V+........^T.z|
    00000070 c3 56 f5 57 9e 0a 91 ef 24 79 be 02 7d 03 ec 24 |.V.W....$y..}..$|
    00000080 fd 25 95 c3 dc 75 7c 1f 66 02 a6 e1 20 6a 36 79 |.%...u|.f... j6y|
    00000090 f5 24 57 8c ac 2c a2 04 7f e0 50 89 28 33 91 42 |.$W..,....P.(3.B|
    000000a0 50 6e 4a 46 e5 06 d9 dd af 03 fc ea f8 8e 73 ec |PnJF..........s.|
    000000b0 6a 80 84 e2 6b 23 2f e8 ca e6 3d db bc 2b fb 07 |j...k#/...=..+..|
    000000c0 dd b4 6a 8a 50 79 c6 2b e1 dd 0c 9e d4 08 67 68 |..j.Py.+......gh|
    000000d0 ed a8 e2 70 cf e8 4d 22 fa a1 88 13 42 7e 64 32 |...p..M"....B~d2|
    000000e0 97 df 19 bf 15 ad a5 f3 de 05 14 93 7b 27 bd e7 |............{'..|
    000000f0 10 53 ca cf 80 79 37 95 ff df 24 5c 23 e1 85 03 |.S...y7...$\#...|

    {"time":"2023-09-16T19:23:25.665662329+02:00","level":"INFO","msg":"S -> C: Session proof"}
    00000000 ae 6b 6d 7f 0c c3 8b 74 42 9b fe cc 07 fa 33 88 |.km....tB.....3.|
    00000010 8b dc aa 66 90 1e 45 c7 f2 88 e0 c2 c8 bb a7 c7 |...f..E.........|
    00000020 a6 2f e8 bd 2a 4d 76 25 26 dc a3 ad 3e 4f f4 ab |./..*Mv%&...>O..|
    00000030 99 ff 0f 80 77 9d 3e bb 4d 03 79 73 d7 ed 66 57 |....w.>.M.ys..fW|
    00000040 44 40 ec fc ba 11 08 cd b3 a0 c1 a3 ec f0 20 d4 |D@............ .|
    00000050 1a 61 40 41 a7 b2 9e 88 38 6f e2 56 58 a9 2d 18 |[email protected].|

    {"time":"2023-09-16T19:23:25.665825205+02:00","level":"INFO","msg":"C -> S : Session proof validation"}
    00000000 e6 12 de a2 bd 1c bd 59 af bd c7 eb 80 27 b7 97 |.......Y.....'..|
    00000010 f8 0d db 9f 86 36 30 40 59 ab c2 15 fb d6 02 08 |.....60@Y.......|

    {"time":"2023-09-16T19:23:25.66588808+02:00","level":"INFO","msg":"S: Client Authenticated?"}
    ```
  8. Zenithar renamed this gist Sep 16, 2023. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  9. Zenithar renamed this gist Sep 16, 2023. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  10. Zenithar renamed this gist Sep 16, 2023. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  11. Zenithar revised this gist Sep 16, 2023. 1 changed file with 6 additions and 0 deletions.
    6 changes: 6 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,9 @@
    # Zero Knowledge based authentication

    Based on
    * https://www.cossacklabs.com/blog/introducing_secure_comparator/
    * https://www.cossacklabs.com/files/secure-comparator-paper-rev12.pdf

    ```
    {"time":"2023-09-16T19:23:25.663348882+02:00","level":"INFO","msg":"C -> S: Session boostrap"}
    {"time":"2023-09-16T19:23:25.664432655+02:00","level":"INFO","msg":"S -> C: Session public keys"}
  12. Zenithar created this gist Sep 16, 2023.
    48 changes: 48 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,48 @@
    ```
    {"time":"2023-09-16T19:23:25.663348882+02:00","level":"INFO","msg":"C -> S: Session boostrap"}
    {"time":"2023-09-16T19:23:25.664432655+02:00","level":"INFO","msg":"S -> C: Session public keys"}
    00000000 69 c4 9d 53 f7 ac e9 13 5a 7b 1f af a9 80 5c 2a |i..S....Z{....\*|
    00000010 87 f4 94 e0 a3 5a 88 87 0a ac 86 d2 d5 ab de 48 |.....Z.........H|
    00000020 60 a4 b4 bb 99 30 34 e3 04 d7 31 38 7a 1b 68 2e |`....04...18z.h.|
    00000030 38 f6 c0 40 0e 6a 18 54 ad f7 41 83 27 d2 97 5e |[email protected].'..^|
    00000040 fe 03 19 6b 16 02 e8 a1 4a 5a 35 f6 80 35 08 a1 |...k....JZ5..5..|
    00000050 b3 4f fb 87 93 38 ac ca 15 a3 44 d3 7c 92 fc 09 |.O...8....D.|...|
    00000060 b7 b8 18 0e 53 e5 1f 61 30 0c bf 82 fb 0d 5d 20 |....S..a0.....] |
    00000070 b5 dd 8f 15 e4 fa 52 c1 15 b7 5c 62 39 6d 0a 8c |......R...\b9m..|
    00000080 ed df e3 b9 55 d6 a1 56 e7 18 2e a1 6c a5 bd 1c |....U..V....l...|
    00000090 cd cf ab 54 0b ce be 73 e5 eb 24 43 51 f8 66 d5 |...T...s..$CQ.f.|
    000000a0 e6 70 09 ce f4 31 54 47 1a e9 c4 c8 5d b8 92 33 |.p...1TG....]..3|
    000000b0 71 98 ad 3e 65 d6 26 da 6d f0 40 ed 0a 67 2a 09 |q..>e.&[email protected]*.|
    {"time":"2023-09-16T19:23:25.665291918+02:00","level":"INFO","msg":"C -> S: Client proof"}
    00000000 64 58 ad 62 91 af 5a 45 c4 db bb bb 7d 7a 7f e4 |dX.b..ZE....}z..|
    00000010 c9 f7 a9 ab 4d c7 62 83 09 24 cd 23 0a 93 16 74 |....M.b..$.#...t|
    00000020 38 98 ec 56 b9 fc 84 55 30 7f 25 de e6 d5 0d 1b |8..V...U0.%.....|
    00000030 28 65 bf b5 5b 06 ec 3b 5c 33 11 d8 f1 0c 4f 81 |(e..[..;\3....O.|
    00000040 ed 21 9f f2 76 37 ec 91 26 89 9b 67 d3 8a dc 7c |.!..v7..&..g...||
    00000050 b1 9b 88 5c e5 98 6f 1b b4 c8 1d ce f8 2a 45 0c |...\..o......*E.|
    00000060 e1 f1 56 2b 9a ea df 14 af a2 94 f0 5e 54 af 7a |..V+........^T.z|
    00000070 c3 56 f5 57 9e 0a 91 ef 24 79 be 02 7d 03 ec 24 |.V.W....$y..}..$|
    00000080 fd 25 95 c3 dc 75 7c 1f 66 02 a6 e1 20 6a 36 79 |.%...u|.f... j6y|
    00000090 f5 24 57 8c ac 2c a2 04 7f e0 50 89 28 33 91 42 |.$W..,....P.(3.B|
    000000a0 50 6e 4a 46 e5 06 d9 dd af 03 fc ea f8 8e 73 ec |PnJF..........s.|
    000000b0 6a 80 84 e2 6b 23 2f e8 ca e6 3d db bc 2b fb 07 |j...k#/...=..+..|
    000000c0 dd b4 6a 8a 50 79 c6 2b e1 dd 0c 9e d4 08 67 68 |..j.Py.+......gh|
    000000d0 ed a8 e2 70 cf e8 4d 22 fa a1 88 13 42 7e 64 32 |...p..M"....B~d2|
    000000e0 97 df 19 bf 15 ad a5 f3 de 05 14 93 7b 27 bd e7 |............{'..|
    000000f0 10 53 ca cf 80 79 37 95 ff df 24 5c 23 e1 85 03 |.S...y7...$\#...|
    {"time":"2023-09-16T19:23:25.665662329+02:00","level":"INFO","msg":"S -> C: Session proof"}
    00000000 ae 6b 6d 7f 0c c3 8b 74 42 9b fe cc 07 fa 33 88 |.km....tB.....3.|
    00000010 8b dc aa 66 90 1e 45 c7 f2 88 e0 c2 c8 bb a7 c7 |...f..E.........|
    00000020 a6 2f e8 bd 2a 4d 76 25 26 dc a3 ad 3e 4f f4 ab |./..*Mv%&...>O..|
    00000030 99 ff 0f 80 77 9d 3e bb 4d 03 79 73 d7 ed 66 57 |....w.>.M.ys..fW|
    00000040 44 40 ec fc ba 11 08 cd b3 a0 c1 a3 ec f0 20 d4 |D@............ .|
    00000050 1a 61 40 41 a7 b2 9e 88 38 6f e2 56 58 a9 2d 18 |[email protected].|
    {"time":"2023-09-16T19:23:25.665825205+02:00","level":"INFO","msg":"C -> S : Session proof validation"}
    00000000 e6 12 de a2 bd 1c bd 59 af bd c7 eb 80 27 b7 97 |.......Y.....'..|
    00000010 f8 0d db 9f 86 36 30 40 59 ab c2 15 fb d6 02 08 |.....60@Y.......|
    {"time":"2023-09-16T19:23:25.66588808+02:00","level":"INFO","msg":"S: Client Authenticated?"}
    ```
    323 changes: 323 additions & 0 deletions zkpauth.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,323 @@
    package main

    import (
    "crypto/ed25519"
    "crypto/rand"
    "crypto/sha512"
    "encoding/hex"
    "errors"
    "fmt"
    "io"
    "os"

    "log/slog"

    "filippo.io/edwards25519"
    )

    // https://www.cossacklabs.com/blog/introducing_secure_comparator/
    // https://www.cossacklabs.com/files/secure-comparator-paper-rev12.pdf

    type Server struct {
    a2, a3 ed25519.PrivateKey
    g2a, g3a ed25519.PublicKey
    pA, qA, pB, qB *edwards25519.Point
    }

    func (p *Server) Bootstrap() ([]byte, error) {
    // Generate a2, a3
    var err error
    p.g2a, p.a2, err = ed25519.GenerateKey(rand.Reader)
    if err != nil {
    return nil, fmt.Errorf("unable to generate ephemeral key pair: %w", err)
    }
    p.g3a, p.a3, err = ed25519.GenerateKey(rand.Reader)
    if err != nil {
    return nil, fmt.Errorf("unable to generate ephemeral key pair: %w", err)
    }

    var out []byte
    out = append(out, p.g2a...)
    out = append(out, ed25519.Sign(p.a2, p.g2a)...)
    out = append(out, p.g3a...)
    out = append(out, ed25519.Sign(p.a3, p.g3a)...)

    return out, nil
    }

    func (p *Server) Prove(proof, secret []byte) ([]byte, error) {
    // Ensure expected length
    // g2b || g2b signature || g3b || g3b signature || Pb || Qb
    if len(proof) < 2*(ed25519.PublicKeySize+ed25519.SignatureSize)+2*ed25519.PublicKeySize {
    return nil, errors.New("message too short")
    }

    // Ensure valid signatures
    var (
    g2b = proof[:ed25519.PublicKeySize]
    g2bSig = proof[ed25519.PublicKeySize : ed25519.PublicKeySize+ed25519.SignatureSize]
    g3b = proof[ed25519.PublicKeySize+ed25519.SignatureSize : ed25519.PublicKeySize+ed25519.SignatureSize+ed25519.PublicKeySize]
    g3bSig = proof[ed25519.PublicKeySize+ed25519.SignatureSize+ed25519.PublicKeySize : ed25519.PublicKeySize+ed25519.SignatureSize+ed25519.PublicKeySize+ed25519.SignatureSize]
    pb = proof[2*(ed25519.PublicKeySize+ed25519.SignatureSize) : 2*(ed25519.PublicKeySize+ed25519.SignatureSize)+ed25519.PublicKeySize]
    qb = proof[2*(ed25519.PublicKeySize+ed25519.SignatureSize)+ed25519.PublicKeySize : 2*(ed25519.PublicKeySize+ed25519.SignatureSize)+2*ed25519.PublicKeySize]
    )

    if !ed25519.Verify(ed25519.PublicKey(g2b), g2b, g2bSig) {
    return nil, errors.New("invalid proof of possession")
    }
    if !ed25519.Verify(ed25519.PublicKey(g3b), g3b, g3bSig) {
    return nil, errors.New("invalid proof of possession")
    }

    // Compute generator groups
    a2, err := privateToScalar(p.a2)
    if err != nil {
    return nil, fmt.Errorf("unable to initialize a scalar from ephemeral key: %w", err)
    }
    g2bp, err := (&edwards25519.Point{}).SetBytes(g2b)
    if err != nil {
    return nil, fmt.Errorf("unable to initialize a point from ephemeral public key: %w", err)
    }
    G2 := (&edwards25519.Point{}).ScalarMult(a2, g2bp)

    a3, err := privateToScalar(p.a3)
    if err != nil {
    return nil, fmt.Errorf("unable to initialize a scalar from ephemeral key: %w", err)
    }
    g3bp, err := (&edwards25519.Point{}).SetBytes(g3b)
    if err != nil {
    return nil, fmt.Errorf("unable to initialize a point from ephemeral public key: %w", err)
    }
    G3 := (&edwards25519.Point{}).ScalarMult(a3, g3bp)

    var challenge [32]byte
    if _, err := io.ReadFull(rand.Reader, challenge[:]); err != nil {
    return nil, fmt.Errorf("unable to generate random challenge: %w", err)
    }
    s, err := edwards25519.NewScalar().SetBytesWithClamping(challenge[:])
    if err != nil {
    return nil, fmt.Errorf("unable to create a scalar from random challenge: %w", err)
    }

    // Compute secret
    h := sha512.Sum512_256(secret)
    x, err := edwards25519.NewScalar().SetBytesWithClamping(h[:])
    if err != nil {
    return nil, fmt.Errorf("unable to prepare secret as scalar: %w", err)
    }

    //
    p.qB, err = (&edwards25519.Point{}).SetBytes(qb)
    if err != nil {
    return nil, fmt.Errorf("unable to prepare secret public key as point: %w", err)
    }
    p.pB, err = (&edwards25519.Point{}).SetBytes(pb)
    if err != nil {
    return nil, fmt.Errorf("unable to prepare secret public key as point: %w", err)
    }

    p.pA = (&edwards25519.Point{}).ScalarMult(s, G3)
    p.qA = (&edwards25519.Point{}).VarTimeDoubleScalarBaseMult(x, G2, s)
    Ra := (&edwards25519.Point{}).ScalarMult(a3, (&edwards25519.Point{}).Subtract(p.qA, p.qB))

    var out []byte
    out = append(out, p.pA.Bytes()...)
    out = append(out, p.qA.Bytes()...)
    out = append(out, Ra.Bytes()...)

    return out, nil
    }

    func (p *Server) Verify(proof []byte) (bool, error) {
    // Ensure expected length
    // Rb
    if len(proof) < ed25519.PublicKeySize {
    return false, errors.New("message too short")
    }

    a3, err := privateToScalar(p.a3)
    if err != nil {
    return false, fmt.Errorf("unable to initialize a scalar from ephemeral key: %w", err)
    }

    Rb, _ := (&edwards25519.Point{}).SetBytes(proof)
    Rab := (&edwards25519.Point{}).ScalarMult(a3, Rb)

    if (&edwards25519.Point{}).Subtract(p.pA, p.pB).Equal(Rab) != 1 {
    return false, errors.New("invalid proof")
    }

    return true, nil
    }

    type Client struct {
    b2, b3 ed25519.PrivateKey
    g2b, g3b ed25519.PublicKey
    p, q *edwards25519.Point
    }

    func (v *Client) Proof(in, secret []byte) ([]byte, error) {
    // Ensure expected length
    // g2a || g2a signature || g3a || g3a signature
    if len(in) < 2*(ed25519.PublicKeySize+ed25519.SignatureSize) {
    return nil, errors.New("message too short")
    }

    // Ensure valid signatures
    var (
    g2a = in[:ed25519.PublicKeySize]
    g2aSig = in[ed25519.PublicKeySize : ed25519.PublicKeySize+ed25519.SignatureSize]
    g3a = in[ed25519.PublicKeySize+ed25519.SignatureSize : ed25519.PublicKeySize+ed25519.SignatureSize+ed25519.PublicKeySize]
    g3aSig = in[ed25519.PublicKeySize+ed25519.SignatureSize+ed25519.PublicKeySize : ed25519.PublicKeySize+ed25519.SignatureSize+ed25519.PublicKeySize+ed25519.SignatureSize]
    )
    if !ed25519.Verify(ed25519.PublicKey(g2a), g2a, g2aSig) {
    return nil, errors.New("invalid proof of possession")
    }
    if !ed25519.Verify(ed25519.PublicKey(g3a), g3a, g3aSig) {
    return nil, errors.New("invalid proof of possession")
    }

    // Generate b2, b3
    var err error
    v.g2b, v.b2, err = ed25519.GenerateKey(rand.Reader)
    if err != nil {
    return nil, fmt.Errorf("unable to generate ephemeral key pair: %w", err)
    }
    v.g3b, v.b3, err = ed25519.GenerateKey(rand.Reader)
    if err != nil {
    return nil, fmt.Errorf("unable to generate ephemeral key pair: %w", err)
    }

    b2, err := privateToScalar(v.b2)
    if err != nil {
    return nil, fmt.Errorf("unable to initialize a scalar from ephemeral key: %w", err)
    }
    g2ap, _ := (&edwards25519.Point{}).SetBytes(g2a)
    G2 := (&edwards25519.Point{}).ScalarMult(b2, g2ap)

    b3, err := privateToScalar(v.b3)
    if err != nil {
    return nil, fmt.Errorf("unable to initialize a scalar from ephemeral key: %w", err)
    }
    g3ap, _ := (&edwards25519.Point{}).SetBytes(g3a)
    G3 := (&edwards25519.Point{}).ScalarMult(b3, g3ap)

    var challenge [32]byte
    if _, err := io.ReadFull(rand.Reader, challenge[:]); err != nil {
    return nil, fmt.Errorf("unable to generate random challenge: %w", err)
    }
    r, _ := edwards25519.NewScalar().SetBytesWithClamping(challenge[:])

    // Compute secret
    h := sha512.Sum512_256(secret)
    y, err := edwards25519.NewScalar().SetBytesWithClamping(h[:])
    if err != nil {
    return nil, fmt.Errorf("unable to prepare secret as scalar: %w", err)
    }

    v.p = (&edwards25519.Point{}).ScalarMult(r, G3)
    v.q = (&edwards25519.Point{}).VarTimeDoubleScalarBaseMult(y, G2, r)

    var out []byte
    out = append(out, v.g2b...)
    out = append(out, ed25519.Sign(v.b2, v.g2b)...)
    out = append(out, v.g3b...)
    out = append(out, ed25519.Sign(v.b3, v.g3b)...)
    out = append(out, v.p.Bytes()...)
    out = append(out, v.q.Bytes()...)

    return out, nil
    }

    func (v *Client) Verify(proof []byte) ([]byte, error) {
    // Ensure expected length
    // Pa || Qa || Ra
    if len(proof) < 3*(ed25519.PublicKeySize) {
    return nil, errors.New("message too short")
    }

    // Ensure valid signatures
    var (
    pa = proof[:ed25519.PublicKeySize]
    qa = proof[ed25519.PublicKeySize : 2*ed25519.PublicKeySize]
    ra = proof[2*ed25519.PublicKeySize : 3*ed25519.PublicKeySize]
    )

    b3, err := privateToScalar(v.b3)
    if err != nil {
    return nil, fmt.Errorf("unable to initialize a scalar from ephemeral key: %w", err)
    }

    Pa, _ := (&edwards25519.Point{}).SetBytes(pa)
    Qa, _ := (&edwards25519.Point{}).SetBytes(qa)
    Ra, _ := (&edwards25519.Point{}).SetBytes(ra)

    Rb := (&edwards25519.Point{}).ScalarMult(b3, (&edwards25519.Point{}).Subtract(Qa, v.q))
    Rab := (&edwards25519.Point{}).ScalarMult(b3, Ra)

    if (&edwards25519.Point{}).Subtract(Pa, v.p).Equal(Rab) != 1 {
    return nil, errors.New("invalid proof")
    }

    return Rb.Bytes(), nil
    }

    func privateToScalar(pk ed25519.PrivateKey) (*edwards25519.Scalar, error) {
    h := sha512.Sum512(pk.Seed())
    s, err := edwards25519.NewScalar().SetBytesWithClamping(h[:32])
    return s, err
    }

    func main() {
    logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{}))
    slog.SetDefault(logger)

    c1 := &Client{}
    logger.Info("C -> S: Session boostrap")

    session := &Server{}
    step1, err := session.Bootstrap()
    if err != nil {
    logger.Error("unable to initialize prover", "error", err)
    return
    }

    logger.Info("S -> C: Session public keys")
    fmt.Println(hex.Dump(step1))

    step2, err := c1.Proof(step1, []byte("very-secret-password"))
    if err != nil {
    logger.Error("unable to initialize prover", "error", err)
    return
    }

    logger.Info("C -> S: Client proof")
    fmt.Println(hex.Dump(step2))

    step3, err := session.Prove(step2, []byte("very-secret-password"))
    if err != nil {
    logger.Error("unable to compute proof", "error", err)
    return
    }

    logger.Info("S -> C: Session proof")
    fmt.Println(hex.Dump(step3))

    step4, err := c1.Verify(step3)
    if err != nil {
    logger.Error("unable to validate proof", "error", err)
    return
    }

    logger.Info("C -> S : Authenticate session proof")
    fmt.Println(hex.Dump(step4))

    valid, err := session.Verify(step4)
    if err != nil {
    logger.Error("unable to authenticate proof", "error", err)
    return
    }

    logger.Info("S: Client Authenticated?")
    fmt.Println(valid)
    }