@@ -42,6 +42,23 @@ func main() {
4242 },
4343 },
4444 },
45+ {
46+ Name : "nextephemeral" ,
47+ Usage : "A helper to compute the next ephemeral key " +
48+ "given the current ephemeral key and a " +
49+ "private key" ,
50+ Action : nextEphemeral ,
51+ Flags : []cli.Flag {
52+ cli.StringFlag {
53+ Name : "priv" ,
54+ Required : true ,
55+ },
56+ cli.StringFlag {
57+ Name : "pub" ,
58+ Required : true ,
59+ },
60+ },
61+ },
4562 {
4663 Name : "generate" ,
4764 Usage : "Build a new onion." ,
@@ -207,44 +224,58 @@ func generate(ctx *cli.Context) error {
207224type onionInfo struct {
208225 SessionKey string `json:"session_key"`
209226 AssociatedData string `json:"associated_data"`
227+ BlindingPoint string `json:"blinding_point"`
210228 Onion string `json:"onion"`
211229}
212230
213231func parseOnionInfo (info * onionInfo ) (* sphinx.OnionPacket , * btcec.PrivateKey ,
214- []byte , error ) {
232+ []byte , * btcec. PublicKey , error ) {
215233
216234 sessionKeyBytes , err := hex .DecodeString (info .SessionKey )
217235 if err != nil {
218- return nil , nil , nil , fmt .Errorf ("unable to decode the " +
236+ return nil , nil , nil , nil , fmt .Errorf ("unable to decode the " +
219237 "sessionKey %v: %v" , info .SessionKey , err )
220238 }
221239
222240 if len (sessionKeyBytes ) != 32 {
223- return nil , nil , nil , fmt .Errorf ("session priv key must be " +
224- "32 bytes long" )
241+ return nil , nil , nil , nil , fmt .Errorf ("session priv key must " +
242+ "be 32 bytes long" )
225243 }
226244
227245 sessionKey , _ := btcec .PrivKeyFromBytes (sessionKeyBytes )
228246
229247 assocData , err := hex .DecodeString (info .AssociatedData )
230248 if err != nil {
231- return nil , nil , nil , fmt .Errorf ("unable to decode the " +
249+ return nil , nil , nil , nil , fmt .Errorf ("unable to decode the " +
232250 "associate data %v: %v" , info .AssociatedData , err )
233251 }
234252
235253 onion , err := hex .DecodeString (info .Onion )
236254 if err != nil {
237- return nil , nil , nil , fmt .Errorf ("unable to decode the " +
255+ return nil , nil , nil , nil , fmt .Errorf ("unable to decode the " +
238256 "onion %v: %v" , info .Onion , err )
239257 }
240258
241259 var packet sphinx.OnionPacket
242260 err = packet .Decode (bytes .NewBuffer (onion ))
243261 if err != nil {
244- return nil , nil , nil , err
262+ return nil , nil , nil , nil , err
245263 }
246264
247- return & packet , sessionKey , assocData , nil
265+ var blindingPoint * btcec.PublicKey
266+ if info .BlindingPoint != "" {
267+ bpBytes , err := hex .DecodeString (info .BlindingPoint )
268+ if err != nil {
269+ return nil , nil , nil , nil , err
270+ }
271+
272+ blindingPoint , err = btcec .ParsePubKey (bpBytes )
273+ if err != nil {
274+ return nil , nil , nil , nil , err
275+ }
276+ }
277+
278+ return & packet , sessionKey , assocData , blindingPoint , nil
248279}
249280
250281func peel (ctx * cli.Context ) error {
@@ -260,7 +291,9 @@ func peel(ctx *cli.Context) error {
260291 return err
261292 }
262293
263- packet , sessionKey , assocData , err := parseOnionInfo (& info )
294+ packet , sessionKey , assocData , blindingPoint , err := parseOnionInfo (
295+ & info ,
296+ )
264297 if err != nil {
265298 return err
266299 }
@@ -272,7 +305,9 @@ func peel(ctx *cli.Context) error {
272305 s .Start ()
273306 defer s .Stop ()
274307
275- p , err := s .ProcessOnionPacket (packet , assocData , 10 )
308+ p , err := s .ProcessOnionPacket (
309+ packet , assocData , 10 , sphinx .WithBlindingPoint (blindingPoint ),
310+ )
276311 if err != nil {
277312 return err
278313 }
@@ -286,3 +321,36 @@ func peel(ctx *cli.Context) error {
286321
287322 return nil
288323}
324+
325+ func nextEphemeral (ctx * cli.Context ) error {
326+ privKeyByte , err := hex .DecodeString (ctx .String ("priv" ))
327+ if err != nil {
328+ return err
329+ }
330+ if len (privKeyByte ) != 32 {
331+ return fmt .Errorf ("private key must be 32 bytes" )
332+ }
333+
334+ privKey , _ := btcec .PrivKeyFromBytes (privKeyByte )
335+
336+ pubKeyBytes , err := hex .DecodeString (ctx .String ("pub" ))
337+ if err != nil {
338+ return err
339+ }
340+
341+ pubKey , err := btcec .ParsePubKey (pubKeyBytes )
342+ if err != nil {
343+ return err
344+ }
345+
346+ nextBlindedKey , err := sphinx .NextEphemeral (
347+ & sphinx.PrivKeyECDH {PrivKey : privKey }, pubKey ,
348+ )
349+ if err != nil {
350+ return err
351+ }
352+
353+ fmt .Printf ("%x\n " , nextBlindedKey .SerializeCompressed ())
354+
355+ return nil
356+ }
0 commit comments