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 }