github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/protocol/v2/secure_request.go (about) 1 // Copyright (c) 2022, R.I. Pienaar and the Choria Project contributors 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 5 package v2 6 7 import ( 8 "context" 9 "encoding/json" 10 "fmt" 11 "strings" 12 "sync" 13 14 "github.com/choria-io/go-choria/inter" 15 "github.com/choria-io/go-choria/protocol" 16 log "github.com/sirupsen/logrus" 17 ) 18 19 // SecureRequest contains 1 serialized Request signed and with the related JWTs attached 20 type SecureRequest struct { 21 // The protocol version for this secure request `io.choria.protocol.v2.secure_request` / protocol.SecureRequestV2 22 Protocol protocol.ProtocolVersion `json:"protocol"` 23 // The request held in the Secure Request 24 MessageBody []byte `json:"request"` 25 // A signature made using the ed25519 seed of the caller or signer 26 Signature []byte `json:"signature"` 27 // The JWT of the caller 28 CallerJWT string `json:"caller"` 29 // The JWT of the delegated signer, present when the AAA server is used 30 SignerJWT string `json:"signer,omitempty"` 31 32 security inter.SecurityProvider 33 mu sync.Mutex 34 } 35 36 // NewSecureRequest creates a choria:secure:request:1 37 func NewSecureRequest(request protocol.Request, security inter.SecurityProvider) (protocol.SecureRequest, error) { 38 if security.BackingTechnology() != inter.SecurityTechnologyED25519JWT { 39 return nil, ErrIncorrectProtocol 40 } 41 42 secure := &SecureRequest{ 43 Protocol: protocol.SecureRequestV2, 44 security: security, 45 } 46 47 // TODO: we might choose to only support secure mode, but complicated with provisioning 48 if protocol.IsSecure() { 49 token, err := security.TokenBytes() 50 if err != nil { 51 return nil, err 52 } 53 54 secure.CallerJWT = string(token) 55 } 56 57 err := secure.SetMessage(request) 58 if err != nil { 59 return nil, err 60 } 61 62 return secure, nil 63 } 64 65 // NewRemoteSignedSecureRequest is a NewSecureRequest that delegates the signing to a remote signer like aaasvc 66 func NewRemoteSignedSecureRequest(ctx context.Context, request protocol.Request, security inter.SecurityProvider) (protocol.SecureRequest, error) { 67 if security.BackingTechnology() != inter.SecurityTechnologyED25519JWT { 68 return nil, ErrIncorrectProtocol 69 } 70 71 // no need for remote stuff, we don't do any signing or certs, 72 // additionally the service hosting the remote signing service isnt 73 // secured by choria protocol since at calling time the client does 74 // not have a cert etc, but the request expects a signed JWT so that 75 // provides the security of that request 76 // 77 // TODO: we might choose to only support secure mode, but complicated with provisioning, provisioning though shouldnt make requests 78 if !protocol.IsSecure() || protocol.IsRemoteSignerAgent(request.Agent()) { 79 return NewSecureRequest(request, security) 80 } 81 82 reqj, err := request.JSON() 83 if err != nil { 84 return nil, err 85 } 86 87 secj, err := security.RemoteSignRequest(ctx, reqj) 88 if err != nil { 89 return nil, err 90 } 91 92 secure := &SecureRequest{ 93 Protocol: protocol.SecureRequestV2, 94 security: security, 95 } 96 97 err = json.Unmarshal(secj, &secure) 98 if err != nil { 99 return nil, fmt.Errorf("could not parse signed request: %s", err) 100 } 101 102 if secure.SignerJWT == "" { 103 return nil, fmt.Errorf("remote signer did not set a signer JWT") 104 } 105 106 // We set our caller token here despite having done a delegated request because the 107 // delegation would not know our token, so we just set this here which will not modify 108 // the secure payload or its signature etc 109 // 110 // TODO: we might choose to only support secure mode, but complicated with provisioning 111 if protocol.IsSecure() { 112 token, err := security.TokenBytes() 113 if err != nil { 114 return nil, err 115 } 116 117 secure.CallerJWT = string(token) 118 } 119 120 return secure, nil 121 } 122 123 // NewSecureRequestFromTransport creates a new choria:secure:request:1 from the data contained in a Transport message 124 func NewSecureRequestFromTransport(message protocol.TransportMessage, security inter.SecurityProvider, skipvalidate bool) (protocol.SecureRequest, error) { 125 if security.BackingTechnology() != inter.SecurityTechnologyED25519JWT { 126 return nil, ErrIncorrectProtocol 127 } 128 129 secure := &SecureRequest{ 130 Protocol: protocol.SecureRequestV2, 131 security: security, 132 } 133 134 data, err := message.Message() 135 if err != nil { 136 return nil, err 137 } 138 139 err = secure.IsValidJSON(data) 140 if err != nil { 141 return nil, fmt.Errorf("the JSON body from the TransportMessage is not a valid SecureRequest: %w", err) 142 } 143 144 err = json.Unmarshal(data, &secure) 145 if err != nil { 146 return nil, err 147 } 148 149 if !skipvalidate { 150 if !secure.Valid() { 151 return nil, fmt.Errorf("secure request messages created from Transport Message did not pass security validation") 152 } 153 } 154 155 return secure, nil 156 } 157 158 func (r *SecureRequest) SetMessage(request protocol.Request) error { 159 r.mu.Lock() 160 defer r.mu.Unlock() 161 162 j, err := request.JSON() 163 if err != nil { 164 protocolErrorCtr.Inc() 165 return fmt.Errorf("could not JSON encode reply message to store it in the Secure Request: %w", err) 166 } 167 168 r.Signature = []byte("insecure") 169 170 // TODO: we might choose to only support secure mode, but complicated with provisioning 171 if protocol.IsSecure() && !protocol.IsRemoteSignerAgent(request.Agent()) { 172 sig, err := r.security.SignBytes(j) 173 if err != nil { 174 return err 175 } 176 r.Signature = sig 177 } 178 179 r.MessageBody = j 180 181 return nil 182 } 183 184 func (r *SecureRequest) Valid() bool { 185 // TODO: we might choose to only support secure mode, but complicated with provisioning 186 if !protocol.IsSecure() { 187 log.Debug("Bypassing validation on secure request due to build time flags") 188 return true 189 } 190 191 jwts := [][]byte{[]byte(r.CallerJWT)} 192 // delegated signatures 193 if r.SignerJWT != "" { 194 jwts = append(jwts, []byte(r.SignerJWT)) 195 } 196 197 should, _ := r.security.VerifySignatureBytes(r.MessageBody, r.Signature, jwts...) 198 if !should { 199 log.Errorf("Signature in request did not pass validation") 200 invalidCtr.Inc() 201 return false 202 } 203 204 req, err := NewRequestFromSecureRequest(r) 205 if err != nil { 206 log.Errorf("Could not create Request to validate Secure Request with: %s", err) 207 protocolErrorCtr.Inc() 208 return false 209 } 210 211 _, err = r.security.ShouldAllowCaller(req.CallerID(), jwts...) 212 if err != nil { 213 log.Errorf("Caller verification failed: %s", err) 214 protocolErrorCtr.Inc() 215 return false 216 } 217 218 validCtr.Inc() 219 220 return true 221 } 222 223 func (r *SecureRequest) JSON() ([]byte, error) { 224 r.mu.Lock() 225 j, err := json.Marshal(r) 226 r.mu.Unlock() 227 if err != nil { 228 protocolErrorCtr.Inc() 229 return nil, fmt.Errorf("could not JSON Marshal: %s", err) 230 } 231 232 if err = r.IsValidJSON(j); err != nil { 233 return nil, fmt.Errorf("%w: %s", ErrInvalidJSON, err) 234 } 235 236 return j, nil 237 } 238 239 func (r *SecureRequest) Version() protocol.ProtocolVersion { 240 r.mu.Lock() 241 defer r.mu.Unlock() 242 243 return r.Protocol 244 } 245 246 func (r *SecureRequest) IsValidJSON(data []byte) error { 247 _, errors, err := schemaValidate(protocol.SecureRequestV2, data) 248 if err != nil { 249 protocolErrorCtr.Inc() 250 return fmt.Errorf("could not validate SecureRequest JSON data: %s", err) 251 } 252 253 if len(errors) != 0 { 254 return fmt.Errorf("%w: %s", ErrInvalidJSON, strings.Join(errors, ", ")) 255 } 256 257 return nil 258 } 259 260 func (r *SecureRequest) Message() []byte { 261 r.mu.Lock() 262 defer r.mu.Unlock() 263 264 return r.MessageBody 265 } 266 267 func (r *SecureRequest) SetSigner(signer []byte) error { 268 r.mu.Lock() 269 defer r.mu.Unlock() 270 271 r.SignerJWT = string(signer) 272 273 return nil 274 } 275 276 func (r *SecureRequest) CallerPublicData() string { 277 r.mu.Lock() 278 defer r.mu.Unlock() 279 280 return r.CallerJWT 281 }