github.com/klaytn/klaytn@v1.12.1/crypto/secp256k1/schnorr.go (about) 1 // Copyright 2018 The klaytn Authors 2 // 3 // This file is part of the klaytn library. 4 // 5 // The klaytn library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The klaytn library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the klaytn library. If not, see <http://www.gnu.org/licenses/>. 17 18 package secp256k1 19 20 import ( 21 "bytes" 22 "crypto/sha256" 23 "sort" 24 ) 25 26 // SchnorrSignSingle digitally signs the input message using Schnorr signature scheme. 27 func SchnorrSignSingle(G *BitCurve, msg, x, P []byte) ([]byte, []byte) { 28 // [Schnorr Signature 101] 29 // 30 // 1) Deriving public key from private key 31 // : P = x * G 32 // : P is a corresponding public key 33 // : x is a private key 34 // : G is a curve base point 35 // 36 // 2) Creating a random scalar for signing 37 // : k = H(m || x) 38 // 39 // 3) Compute the first part of Schnorr signature, R 40 // : R = k * G 41 // 42 // The goal is to make R = s * G + e * P for some s and e. 43 // 44 // 4) Compute e 45 // : e = H(m || P || R) 46 // 47 // 5) Find s 48 // Recall P = x * G; therefore 49 // R = s * G + e * P 50 // = s * G + e * x * G 51 // = (s + e * x) * G 52 // k * G = (s + e * x) * G 53 // k = s + e * x 54 // s = k - e * x 55 // 56 // Finally, (R, s) is your Schnorr signature 57 58 k := hash(msg, x) // k = H(m || x) 59 R := G.Marshal(G.ScalarBaseMult(k)) // R = k * G 60 61 e := hash(msg, P, R) // e = H(m || P || R) 62 63 ex := ScMul(e, x) // e * x 64 s := ScSub(k, ex) // s = k - e * x 65 66 return R, s 67 } 68 69 // SchnorrVerifySingle verifies a Schnorr signature. 70 // Note that this implementation is relatively slow compared to the C implementation. Use this sparingly. 71 func SchnorrVerify(G *BitCurve, msg, R, s, P []byte) bool { 72 e := hash(msg, P, R) 73 74 sx, sy := G.ScalarBaseMult(s) 75 76 Px, Py := G.Unmarshal(P) 77 ePx, ePy := G.ScalarMult(Px, Py, e) 78 79 V := G.Marshal(G.Add(sx, sy, ePx, ePy)) 80 81 return bytes.Equal(R, V) // R == s*G + e*P 82 } 83 84 /* 85 [Brief Overview on Schnorr 2-out-of-2 signature scheme] 86 87 In Schnorr signature, Alice shares (R, s) with R = k * G 88 where k is an arbitrary number and G is an elliptic curve group. 89 90 R can be computed from (e, s) via R = s * G + e * P for some e ans s and 91 e can be computed from (R, s) via e = H(m || P || R) 92 93 To sign a message, Alice needs to pick a value for k. For security reason, 94 k should not be reused over multiple messages. 95 96 Given a message m and private key x, we use k = H(m || x) where H is a secure hash function. 97 98 R = k * G 99 = H(m || x) * G 100 101 Recall that R = s * G + e * P. Hence, 102 103 R = s * G + e * P = k * G 104 s * G + e * x * G = k * G 105 (s + e * x) * G = k * G 106 s + e * x = k (*) 107 108 By (*), s = k - e * x (**) 109 110 Suppose Alice and Bob have the following keys: 111 112 Alice: P0 = x0 * G 113 Bob: P1 = x1 * G 114 115 Alice and Bob can safely create a multisig on a message, m, by using generated keys. 116 117 1. C = H(P0 || P1) Let C be the common value that both Alice and Bob use to generate safe keys 118 119 2. Q0 = H(C || P0) * P0 Let Q0 be Alice's public key 120 3. Q1 = H(C || P1) * P1 Let Q1 be Bob's public key 121 4. P = Q0 + Q1 Let P be the public key for the target multisig 122 123 5. y0 = x0 * H(C || P0) Let y0 be Alice's private key 124 6. y1 = x1 * H(C || P1) Let y1 be Bob's private key 125 126 7. k0 = H(m || y0) Alice's random value for m 127 8. k1 = H(m || y1) Bob's random value for m 128 9. R0 = k0 * G 129 10. R1 = k1 * G 130 11. R = R0 + R1 131 132 We use e = H(m || P || R) to harden the security, preventing either Alice or Bob from stealing 133 the control over the other party's key. 134 135 Assuming Alice and Bob have already exchanged Q0, Q1, R0 and R1, Alice and Bob can compute the followings: 136 137 e = H(m || P || R) by (4), (11) 138 139 Using the computed e, Alice and Bob can compute s0 and s1. 140 141 Alice: s0 = k0 - e * y0 142 Bob: s1 = k1 - e * y1 143 144 Once they exchange s0 and s1, they can compute s. 145 146 s = s0 + s1 147 148 Thus, Alice and Bob can publish (R, s) as a signature for m. 149 Verifying (R, s) can be done by computing R = s * G + H(m || P || R) * P. 150 */ 151 152 // ComputeC derives the common value for multiple public keys. 153 func ComputeC(keys ...[]byte) []byte { 154 sort.SliceStable(keys, func(i, j int) bool { 155 return bytes.Compare(keys[i], keys[j]) < 0 156 }) 157 return hash(keys...) 158 } 159 160 // SchnorrSignMultiBootstrap computes an individual share of a Schnorr multi-signature given all public keys. 161 // Q: a dedicated, security hardened public key for this multi-signature party 162 // R: a part of the generating multi-signature for the input publickey 163 // y: a dedicated, security hardened private key for this multi-signature party 164 func SchnorrSignMultiBootstrap(G *BitCurve, msg, privateKey, publicKey []byte, othersPublicKeys ...[]byte) (Q, R, y []byte) { 165 C := ComputeC(append([][]byte{publicKey}, othersPublicKeys...)...) 166 167 z := hash(C, publicKey) 168 169 Px, Py := G.Unmarshal(publicKey) 170 Qx, Qy := G.ScalarMult(Px, Py, z) 171 172 y = ScMul(privateKey, z) 173 174 k := hash(msg, y) 175 176 Rx, Ry := G.ScalarBaseMult(k) 177 178 Q = G.Marshal(Qx, Qy) 179 R = G.Marshal(Rx, Ry) 180 return 181 } 182 183 // SchnorrSignMultiComputeS computes the s part of a Schnorr multi-signature (i.e., s in (R, s)). 184 func SchnorrSignMultiComputeS(msg, P, R, y []byte) []byte { 185 e := hash(msg, P, R) 186 k := hash(msg, y) 187 return ScSub(k, ScMul(e, y)) 188 } 189 190 // Simple helper function concatenating all input bytes and summing it up with SHA256. 191 func hash(bytes ...[]byte) []byte { 192 h := sha256.New() 193 for _, b := range bytes { 194 h.Write(b) 195 } 196 result := h.Sum(nil) 197 return result[:] 198 }