|
15 | 15 | package firmware
|
16 | 16 |
|
17 | 17 | import (
|
| 18 | + "bytes" |
18 | 19 | "crypto/sha256"
|
19 | 20 | "math/big"
|
20 | 21 |
|
@@ -57,3 +58,60 @@ func antikleptoVerify(hostNonce, signerCommitment, signature []byte) error {
|
57 | 58 | }
|
58 | 59 | return nil
|
59 | 60 | }
|
| 61 | + |
| 62 | +// Verifies a DLEQ proof. |
| 63 | +// |
| 64 | +// A DLEQ (discrete log equivalence) proof proves that the discrete log of p1 to the secp256k1 base |
| 65 | +// G is the same as the discrete log of p2 to another base gen2. |
| 66 | +// |
| 67 | +// Same as |
| 68 | +// https://github.com/BlockstreamResearch/secp256k1-zkp/blob/6152622613fdf1c5af6f31f74c427c4e9ee120ce/src/modules/ecdsa_adaptor/dleq_impl.h#L129 |
| 69 | +// with default noncefp and ndata==NULL). |
| 70 | +func DLEQVerify(proof []byte, p1, gen2, p2 *btcec.PublicKey) error { |
| 71 | + if len(proof) != 64 { |
| 72 | + return errp.New("proof must be 64 bytes") |
| 73 | + } |
| 74 | + s := proof[:32] |
| 75 | + e := proof[32:] |
| 76 | + curve := btcec.S256() |
| 77 | + |
| 78 | + // R1 = s*G - e*P1 |
| 79 | + sPubX, sPubY := curve.ScalarBaseMult(s) |
| 80 | + eP1X, eP1Y := curve.ScalarMult(p1.X(), p1.Y(), e) |
| 81 | + // Negate eP1 |
| 82 | + eP1Y = new(big.Int).Sub(curve.P, eP1Y) |
| 83 | + r1PubX, r1PubY := curve.Add(sPubX, sPubY, eP1X, eP1Y) |
| 84 | + |
| 85 | + /* R2 = s*gen2 - e*P2 */ |
| 86 | + sGen2X, sGen2Y := curve.ScalarMult(gen2.X(), gen2.Y(), s) |
| 87 | + eP2X, eP2Y := curve.ScalarMult(p2.X(), p2.Y(), e) |
| 88 | + // Negate eP2 |
| 89 | + eP2Y = new(big.Int).Sub(curve.P, eP2Y) |
| 90 | + r2PubX, r2PubY := curve.Add(sGen2X, sGen2Y, eP2X, eP2Y) |
| 91 | + |
| 92 | + toPub := func(x, y *big.Int) *btcec.PublicKey { |
| 93 | + var xx, yy btcec.FieldVal |
| 94 | + xx.SetByteSlice(x.Bytes()) |
| 95 | + yy.SetByteSlice(y.Bytes()) |
| 96 | + return btcec.NewPublicKey(&xx, &yy) |
| 97 | + } |
| 98 | + challenge := func() *big.Int { |
| 99 | + var b bytes.Buffer |
| 100 | + b.Write(p1.SerializeCompressed()) |
| 101 | + b.Write(gen2.SerializeCompressed()) |
| 102 | + b.Write(p2.SerializeCompressed()) |
| 103 | + b.Write(toPub(r1PubX, r1PubY).SerializeCompressed()) |
| 104 | + b.Write(toPub(r2PubX, r2PubY).SerializeCompressed()) |
| 105 | + hash := taggedSha256([]byte("DLEQ"), b.Bytes()) |
| 106 | + return new(big.Int).SetBytes(hash) |
| 107 | + } |
| 108 | + modEqual := func(x, y, n *big.Int) bool { |
| 109 | + return new(big.Int).Mod(x, n).Cmp(new(big.Int).Mod(y, n)) == 0 |
| 110 | + } |
| 111 | + eExpected := challenge() |
| 112 | + eInt := new(big.Int).SetBytes(e) |
| 113 | + if !modEqual(eExpected, eInt, curve.N) { |
| 114 | + return errp.New("DLEQ proof verification failed") |
| 115 | + } |
| 116 | + return nil |
| 117 | +} |
0 commit comments