github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/providers/agent/mcorpc/golang/provision/util.go (about)

     1  // Copyright (c) 2021, R.I. Pienaar and the Choria Project contributors
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package provision
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/x509"
    10  	"encoding/hex"
    11  	"encoding/pem"
    12  	"fmt"
    13  	"time"
    14  
    15  	"github.com/choria-io/go-choria/build"
    16  	"github.com/choria-io/go-choria/choria"
    17  	"github.com/choria-io/go-choria/providers/agent/mcorpc"
    18  )
    19  
    20  func abort(msg string, reply *mcorpc.Reply) {
    21  	reply.Statuscode = mcorpc.Aborted
    22  	reply.Statusmsg = msg
    23  }
    24  
    25  func checkToken(token string, reply *mcorpc.Reply) bool {
    26  	if build.ProvisionToken == "" {
    27  		return true
    28  	}
    29  
    30  	if token != build.ProvisionToken {
    31  		log.Errorf("Incorrect Provisioning Token %s given", token)
    32  		abort("Incorrect provision token supplied", reply)
    33  		return false
    34  	}
    35  
    36  	return true
    37  }
    38  
    39  func updateECDHLocked() error {
    40  	var err error
    41  
    42  	ecdhPrivate, ecdhPublic, err = choria.ECDHKeyPair()
    43  
    44  	return err
    45  }
    46  
    47  func ecdhSharedSecretLocked(provisionerPub string) ([]byte, error) {
    48  	pb, err := hex.DecodeString(provisionerPub)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	if len(ecdhPrivate) == 0 {
    54  		return nil, fmt.Errorf("private key not set")
    55  	}
    56  
    57  	return choria.ECDHSharedSecret(ecdhPrivate, pb)
    58  }
    59  
    60  func decryptPrivateKey(privateKey string, ecdhPublic string) ([]byte, error) {
    61  	if len(ecdhPublic) == 0 {
    62  		return nil, fmt.Errorf("no ECDH Public Key")
    63  	}
    64  
    65  	if len(privateKey) == 0 {
    66  		return nil, fmt.Errorf("no Private Key")
    67  	}
    68  
    69  	secret, err := ecdhSharedSecretLocked(ecdhPublic)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	block, _ := pem.Decode([]byte(privateKey))
    75  	if block == nil {
    76  		return nil, fmt.Errorf("bad key received")
    77  	}
    78  
    79  	//lint:ignore SA1019 there is no alternative
    80  	if !x509.IsEncryptedPEMBlock(block) {
    81  		return nil, fmt.Errorf("key is not encrypted")
    82  	}
    83  
    84  	decBlock, err := x509.DecryptPEMBlock(block, secret) //lint:ignore SA1019 there is no alternative
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	out := &bytes.Buffer{}
    90  	err = pem.Encode(out, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: decBlock})
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	return out.Bytes(), nil
    96  }
    97  
    98  func isLockedFor(sender string, caller string) bool {
    99  	if time.Until(lockedUntil) <= 0 {
   100  		return false
   101  	}
   102  
   103  	return lockedBy == fmt.Sprintf("%s.%s", sender, caller)
   104  }
   105  
   106  func lockFor(sender string, caller string) (bool, time.Time) {
   107  	lockMu.Lock()
   108  	defer lockMu.Unlock()
   109  
   110  	// caller holds the lock, extend it
   111  	if isLockedFor(sender, caller) {
   112  		lockedUntil = time.Now().Add(LockWindow).UTC()
   113  		return true, lockedUntil
   114  	}
   115  
   116  	// its locked by someone else
   117  	if time.Until(lockedUntil) > 0 {
   118  		return false, lockedUntil
   119  	}
   120  
   121  	// its not locked so we lock it for the caller
   122  	lockedUntil = time.Now().Add(LockWindow).UTC()
   123  	lockedBy = fmt.Sprintf("%s.%s", sender, caller)
   124  
   125  	return true, lockedUntil
   126  }