@@ -306,22 +306,11 @@ func (c *Client) DoUpdate(ctx context.Context, creds keys.Credentials) ([]byte,
306306 if err != nil {
307307 return nil , nil , nil , nil , fmt .Errorf ("failed to make API call to Defined Networking: %w" , err )
308308 }
309- resultWrapper := message.SignedResponseWrapper {}
310- err = json .Unmarshal (resp , & resultWrapper )
311- if err != nil {
312- return nil , nil , nil , nil , fmt .Errorf ("failed to unmarshal signed response wrapper: %s" , err )
313- }
314309
315310 // Verify the signature
316- valid := false
317- for _ , caPubkey := range creds .TrustedKeys {
318- if caPubkey .Verify (resultWrapper .Data .Message , resultWrapper .Data .Signature ) {
319- valid = true
320- break
321- }
322- }
323- if ! valid {
324- return nil , nil , nil , nil , fmt .Errorf ("failed to verify signed API result" )
311+ resultWrapper , err := verifySignature (resp , creds )
312+ if err != nil {
313+ return nil , nil , nil , nil , err
325314 }
326315
327316 // Consume the verified message
@@ -379,6 +368,130 @@ func (c *Client) DoUpdate(ctx context.Context, creds keys.Credentials) ([]byte,
379368 return result .Config , nebulaPrivkeyPEM , newCreds , meta , nil
380369}
381370
371+ // DoConfigUpdate sends a signed message to the DNClient API to fetch the new configuration update. During this call new keys
372+ // are generated for DNClient API communication. If the API response is successful, the new configuration
373+ // is returned along with the new DNClient API credentials and a meta object.
374+ //
375+ // See dnapi.InsertConfigPrivateKey and dnapi.InsertConfigCert for how to insert the old Nebula cert/private key into the configuration.
376+ func (c * Client ) DoConfigUpdate (ctx context.Context , creds keys.Credentials ) ([]byte , * keys.Credentials , * ConfigMeta , error ) {
377+ // Rotate key
378+ var hostPrivkey keys.PrivateKey // ECDSA
379+
380+ newKeys , err := keys .New ()
381+ if err != nil {
382+ return nil , nil , nil , fmt .Errorf ("failed to generate new keys: %s" , err )
383+ }
384+
385+ msg := message.DoConfigUpdateRequest {
386+ Nonce : nonce (),
387+ }
388+
389+ // Set the correct keypair based on the current private key type
390+ switch creds .PrivateKey .Unwrap ().(type ) {
391+ case ed25519.PrivateKey :
392+ hostPubkeyPEM , err := newKeys .HostEd25519PublicKey .MarshalPEM ()
393+ if err != nil {
394+ return nil , nil , nil , fmt .Errorf ("failed to marshal Ed25519 public key: %s" , err )
395+ }
396+ hostPrivkey = newKeys .HostEd25519PrivateKey
397+ msg .HostPubkeyEd25519 = hostPubkeyPEM
398+ case * ecdsa.PrivateKey :
399+ hostPubkeyPEM , err := newKeys .HostP256PublicKey .MarshalPEM ()
400+ if err != nil {
401+ return nil , nil , nil , fmt .Errorf ("failed to marshal P256 public key: %s" , err )
402+ }
403+ hostPrivkey = newKeys .HostP256PrivateKey
404+ msg .HostPubkeyP256 = hostPubkeyPEM
405+ }
406+
407+ blob , err := json .Marshal (msg )
408+ if err != nil {
409+ return nil , nil , nil , fmt .Errorf ("failed to marshal DNClient message: %s" , err )
410+ }
411+
412+ // Make API call
413+ resp , err := c .postDNClient (ctx , message .DoConfigUpdate , blob , creds .HostID , creds .Counter , creds .PrivateKey )
414+ if err != nil {
415+ return nil , nil , nil , fmt .Errorf ("failed to make API call to Defined Networking: %w" , err )
416+ }
417+
418+ // Verify the signature
419+ resultWrapper , err := verifySignature (resp , creds )
420+ if err != nil {
421+ return nil , nil , nil , err
422+ }
423+
424+ // Consume the verified message
425+ result := message.DoConfigUpdateResponse {}
426+ err = json .Unmarshal (resultWrapper .Data .Message , & result )
427+ if err != nil {
428+ return nil , nil , nil , fmt .Errorf ("failed to unmarshal response (%s): %s" , resultWrapper .Data .Message , err )
429+ }
430+
431+ // Verify the nonce
432+ if ! bytes .Equal (result .Nonce , msg .Nonce ) {
433+ return nil , nil , nil , fmt .Errorf ("nonce mismatch between request (%s) and response (%s)" , msg .Nonce , result .Nonce )
434+ }
435+
436+ // Verify the counter
437+ if result .Counter <= creds .Counter {
438+ return nil , nil , nil , fmt .Errorf ("counter in request (%d) should be less than counter in response (%d)" , creds .Counter , result .Counter )
439+ }
440+
441+ trustedKeys , err := keys .TrustedKeysFromPEM (result .TrustedKeys )
442+ if err != nil {
443+ return nil , nil , nil , fmt .Errorf ("failed to load trusted keys from bundle: %s" , err )
444+ }
445+
446+ newCreds := & keys.Credentials {
447+ HostID : creds .HostID ,
448+ Counter : result .Counter ,
449+ PrivateKey : hostPrivkey ,
450+ TrustedKeys : trustedKeys ,
451+ }
452+
453+ meta := & ConfigMeta {
454+ Org : ConfigOrg {
455+ ID : result .Organization .ID ,
456+ Name : result .Organization .Name ,
457+ },
458+ Network : ConfigNetwork {
459+ ID : result .Network .ID ,
460+ Name : result .Network .Name ,
461+ },
462+ Host : ConfigHost {
463+ ID : result .Host .ID ,
464+ Name : result .Host .Name ,
465+ IPAddress : result .Host .IPAddress ,
466+ },
467+ }
468+
469+ return result .Config , newCreds , meta , nil
470+ }
471+
472+ // verifySignature is a helper function that takes in an API call repsonse message and
473+ // ensures it is signed by a trusted key. It returns the JSON unmarshalled response section
474+ // if the message is valid JSON and the signature is trusted, otherwise it returns an error.
475+ func verifySignature (resp []byte , creds keys.Credentials ) (message.SignedResponseWrapper , error ) {
476+ resultWrapper := message.SignedResponseWrapper {}
477+ err := json .Unmarshal (resp , & resultWrapper )
478+ if err != nil {
479+ return message.SignedResponseWrapper {}, fmt .Errorf ("failed to unmarshal signed response wrapper: %s" , err )
480+ }
481+
482+ valid := false
483+ for _ , caPubkey := range creds .TrustedKeys {
484+ if caPubkey .Verify (resultWrapper .Data .Message , resultWrapper .Data .Signature ) {
485+ valid = true
486+ break
487+ }
488+ }
489+ if ! valid {
490+ return message.SignedResponseWrapper {}, fmt .Errorf ("failed to verify signed API result" )
491+ }
492+ return resultWrapper , nil
493+ }
494+
382495func (c * Client ) CommandResponse (ctx context.Context , creds keys.Credentials , responseToken string , response any ) error {
383496 value , err := json .Marshal (message.CommandResponseRequest {
384497 ResponseToken : responseToken ,
@@ -414,22 +527,9 @@ func (c *Client) Reauthenticate(ctx context.Context, creds keys.Credentials) (*m
414527 return nil , err
415528 }
416529
417- resultWrapper := message.SignedResponseWrapper {}
418- err = json .Unmarshal (resp , & resultWrapper )
530+ resultWrapper , err := verifySignature (resp , creds )
419531 if err != nil {
420- return nil , fmt .Errorf ("failed to unmarshal signed response wrapper: %s" , err )
421- }
422-
423- // Verify the signature
424- valid := false
425- for _ , caPubkey := range creds .TrustedKeys {
426- if caPubkey .Verify (resultWrapper .Data .Message , resultWrapper .Data .Signature ) {
427- valid = true
428- break
429- }
430- }
431- if ! valid {
432- return nil , fmt .Errorf ("failed to verify signed API result" )
532+ return nil , err
433533 }
434534
435535 var response message.ReauthenticateResponse
0 commit comments