88 "encoding/binary"
99 "io"
1010 "io/ioutil"
11+ "math/big"
1112 "sync"
1213
1314 "github.com/aead/chacha20"
@@ -211,37 +212,60 @@ func generateSharedSecrets(paymentPath []*btcec.PublicKey,
211212 // we only need to transmit a single group element, and hops can't link
212213 // a session back to us if they have several nodes in the path.
213214 numHops := len (paymentPath )
214- hopEphemeralPubKeys := make ([]* btcec.PublicKey , numHops )
215215 hopSharedSecrets := make ([][sha256 .Size ]byte , numHops )
216- hopBlindingFactors := make ([][sha256 .Size ]byte , numHops )
217216
218217 // Compute the triplet for the first hop outside of the main loop.
219218 // Within the loop each new triplet will be computed recursively based
220219 // off of the blinding factor of the last hop.
221- hopEphemeralPubKeys [ 0 ] = sessionKey .PubKey ()
220+ lastEphemeralPubKey : = sessionKey .PubKey ()
222221 hopSharedSecrets [0 ] = generateSharedSecret (paymentPath [0 ], sessionKey )
223- hopBlindingFactors [ 0 ] = computeBlindingFactor (hopEphemeralPubKeys [ 0 ] , hopSharedSecrets [0 ][:])
222+ lastBlindingFactor : = computeBlindingFactor (lastEphemeralPubKey , hopSharedSecrets [0 ][:])
224223
225- // Now recursively compute the ephemeral ECDH pub keys, the shared
226- // secret, and blinding factor for each hop.
224+ // The cached blinding factor will contain the running product of the
225+ // session private key x and blinding factors b_i, computed as
226+ // c_0 = x
227+ // c_i = c_{i-1} * b_{i-1} (mod |F(G)|).
228+ // = x * b_0 * b_1 * ... * b_{i-1} (mod |F(G)|).
229+ //
230+ // We begin with just the session private key x, so that base case
231+ // c_0 = x. At the beginning of each iteration, the previous blinding
232+ // factor is aggregated into the modular product, and used as the scalar
233+ // value in deriving the hop ephemeral keys and shared secrets.
234+ var cachedBlindingFactor big.Int
235+ cachedBlindingFactor .SetBytes (sessionKey .D .Bytes ())
236+
237+ // Now recursively compute the cached blinding factor, ephemeral ECDH
238+ // pub keys, and shared secret for each hop.
239+ var nextBlindingFactor big.Int
227240 for i := 1 ; i <= numHops - 1 ; i ++ {
228- // a_{n} = a_{n-1} x c_{n-1} -> (Y_prev_pub_key x prevBlindingFactor)
229- hopEphemeralPubKeys [i ] = blindGroupElement (hopEphemeralPubKeys [i - 1 ],
230- hopBlindingFactors [i - 1 ][:])
231-
232- // s_{n} = sha256( y_{n} x c_{n-1} ) ->
241+ // Update the cached blinding factor with b_{i-1}.
242+ nextBlindingFactor .SetBytes (lastBlindingFactor [:])
243+ cachedBlindingFactor .Mul (& cachedBlindingFactor , & nextBlindingFactor )
244+ cachedBlindingFactor .Mod (& cachedBlindingFactor , btcec .S256 ().Params ().N )
245+
246+ // a_i = g ^ c_i
247+ // = g^( x * b_0 * ... * b_{i-1} )
248+ // = X^( b_0 * ... * b_{i-1} )
249+ // X_our_session_pub_key x all prev blinding factors
250+ lastEphemeralPubKey = blindBaseElement (cachedBlindingFactor .Bytes ())
251+
252+ // e_i = Y_i ^ c_i
253+ // = ( Y_i ^ x )^( b_0 * ... * b_{i-1} )
233254 // (Y_their_pub_key x x_our_priv) x all prev blinding factors
234- yToX := blindGroupElement (paymentPath [i ], sessionKey .D .Bytes ())
235- hopSharedSecrets [i ] = sha256 .Sum256 (
236- multiScalarMult (
237- yToX ,
238- hopBlindingFactors [:i ],
239- ).SerializeCompressed (),
240- )
241-
242- // TODO(roasbeef): prob don't need to store all blinding factors, only the prev...
243- // b_{n} = sha256(a_{n} || s_{n})
244- hopBlindingFactors [i ] = computeBlindingFactor (hopEphemeralPubKeys [i ],
255+ hopBlindedPubKey := blindGroupElement (
256+ paymentPath [i ], cachedBlindingFactor .Bytes ())
257+
258+ // s_i = sha256( e_i )
259+ // = sha256( Y_i ^ (x * b_0 * ... * b_{i-1} )
260+ hopSharedSecrets [i ] = sha256 .Sum256 (hopBlindedPubKey .SerializeCompressed ())
261+
262+ // Only need to evaluate up to the penultimate blinding factor.
263+ if i >= numHops - 1 {
264+ break
265+ }
266+
267+ // b_i = sha256( a_i || s_i )
268+ lastBlindingFactor = computeBlindingFactor (lastEphemeralPubKey ,
245269 hopSharedSecrets [i ][:])
246270 }
247271
@@ -501,13 +525,20 @@ func computeBlindingFactor(hopPubKey *btcec.PublicKey, hopSharedSecret []byte) [
501525 return hash
502526}
503527
504- // blindGroupElement blinds the group element by performing scalar
505- // multiplication of the group element by blindingFactor: G x blindingFactor .
528+ // blindGroupElement blinds the group element P by performing scalar
529+ // multiplication of the group element by blindingFactor: blindingFactor * P .
506530func blindGroupElement (hopPubKey * btcec.PublicKey , blindingFactor []byte ) * btcec.PublicKey {
507531 newX , newY := btcec .S256 ().ScalarMult (hopPubKey .X , hopPubKey .Y , blindingFactor [:])
508532 return & btcec.PublicKey {btcec .S256 (), newX , newY }
509533}
510534
535+ // blindBaseElement blinds the groups's generator G by performing scalar base
536+ // multiplication using the blindingFactor: blindingFactor * G.
537+ func blindBaseElement (blindingFactor []byte ) * btcec.PublicKey {
538+ newX , newY := btcec .S256 ().ScalarBaseMult (blindingFactor )
539+ return & btcec.PublicKey {btcec .S256 (), newX , newY }
540+ }
541+
511542// generateSharedSecret generates the shared secret for a particular hop. The
512543// shared secret is generated by taking the group element contained in the
513544// mix-header, and performing an ECDH operation with the node's long term onion
@@ -523,22 +554,6 @@ func generateSharedSecret(pub *btcec.PublicKey, priv *btcec.PrivateKey) [32]byte
523554 return sha256 .Sum256 (s .SerializeCompressed ())
524555}
525556
526- // multiScalarMult computes the cumulative product of the blinding factors
527- // times the passed public key.
528- //
529- // TODO(roasbeef): optimize using totient?
530- func multiScalarMult (hopPubKey * btcec.PublicKey ,
531- blindingFactors [][sha256 .Size ]byte ) * btcec.PublicKey {
532-
533- finalPubKey := hopPubKey
534-
535- for _ , blindingFactor := range blindingFactors {
536- finalPubKey = blindGroupElement (finalPubKey , blindingFactor [:])
537- }
538-
539- return finalPubKey
540- }
541-
542557// ProcessCode is an enum-like type which describes to the high-level package
543558// user which action should be taken after processing a Sphinx packet.
544559type ProcessCode int
0 commit comments