Files
netbird/upload-server/server/signing.go
T

91 lines
2.1 KiB
Go

package server
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"fmt"
"net/url"
"os"
"strconv"
"time"
log "github.com/sirupsen/logrus"
)
const (
signingKeyVar = "NB_UPLOAD_SIGNING_KEY"
// signatureTTL matches the expiry the S3 backend puts on its presigned URLs.
signatureTTL = 15 * time.Minute
expiryParam = "exp"
signatureParam = "sig"
minSigningKeyLen = 32
)
type signer struct {
key []byte
}
func newSigner() (*signer, error) {
if env, ok := os.LookupEnv(signingKeyVar); ok {
if env == "" {
return nil, fmt.Errorf("%s is set but empty", signingKeyVar)
}
if len(env) < minSigningKeyLen {
return nil, fmt.Errorf("%s must be at least %d bytes", signingKeyVar, minSigningKeyLen)
}
return &signer{key: []byte(env)}, nil
}
key := make([]byte, 32)
if _, err := rand.Read(key); err != nil {
return nil, fmt.Errorf("generate signing key: %w", err)
}
log.Infof("%s not set, generated an ephemeral upload signing key", signingKeyVar)
return &signer{key: key}, nil
}
// sign returns the query parameters that authorize an upload of objectKey.
func (s *signer) sign(objectKey string, now time.Time) url.Values {
exp := now.Add(signatureTTL).Unix()
v := url.Values{}
v.Set(expiryParam, strconv.FormatInt(exp, 10))
v.Set(signatureParam, hex.EncodeToString(s.signature(objectKey, exp)))
return v
}
// verify reports whether query carries a still-valid signature over objectKey.
func (s *signer) verify(objectKey string, query url.Values, now time.Time) error {
exp, err := strconv.ParseInt(query.Get(expiryParam), 10, 64)
if err != nil {
return fmt.Errorf("malformed %s parameter", expiryParam)
}
got, err := hex.DecodeString(query.Get(signatureParam))
if err != nil {
return fmt.Errorf("malformed %s parameter", signatureParam)
}
if !hmac.Equal(got, s.signature(objectKey, exp)) {
return fmt.Errorf("signature mismatch")
}
if now.Unix() >= exp {
return fmt.Errorf("upload URL expired")
}
return nil
}
func (s *signer) signature(objectKey string, exp int64) []byte {
mac := hmac.New(sha256.New, s.key)
fmt.Fprintf(mac, "%s\n%d", objectKey, exp)
return mac.Sum(nil)
}