mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-07 08:57:12 +00:00
115 lines
3.4 KiB
Go
115 lines
3.4 KiB
Go
package licensing
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"errors"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
var ErrMalformedPublicKey = errors.New("malformed license public key")
|
|
|
|
// EmbeddedPublicKey is the production Ed25519 public key (base64 encoded).
|
|
// This is typically set at build time via -ldflags:
|
|
//
|
|
// -X github.com/rcourtman/pulse-go-rewrite/pkg/licensing.EmbeddedPublicKey=BASE64_KEY
|
|
var EmbeddedPublicKey string
|
|
|
|
// PublicKeyFingerprint returns an SHA256 fingerprint for logging.
|
|
func PublicKeyFingerprint(key ed25519.PublicKey) string {
|
|
if len(key) == 0 {
|
|
return ""
|
|
}
|
|
sum := sha256.Sum256(key)
|
|
return "SHA256:" + base64.StdEncoding.EncodeToString(sum[:])
|
|
}
|
|
|
|
// InitEmbeddedPublicKey initializes the runtime license public key using
|
|
// EmbeddedPublicKey and standard environment precedence rules.
|
|
func InitEmbeddedPublicKey() {
|
|
InitPublicKey(EmbeddedPublicKey, isLicenseValidationDevMode(), SetPublicKey)
|
|
}
|
|
|
|
// InitPublicKey initializes the runtime license public key.
|
|
//
|
|
// In release builds (allowEnvOverride=false):
|
|
//
|
|
// Only the compile-time embedded key is used. The PULSE_LICENSE_PUBLIC_KEY
|
|
// env var is ignored to prevent users from substituting their own key and
|
|
// self-signing license JWTs.
|
|
//
|
|
// In dev builds (allowEnvOverride=true):
|
|
// 1. PULSE_LICENSE_PUBLIC_KEY environment variable (base64 encoded)
|
|
// 2. embeddedPublicKey parameter (set at compile time via -ldflags)
|
|
// 3. If devMode=true, skip validation (development only)
|
|
func InitPublicKey(embeddedPublicKey string, devMode bool, setPublicKey func(ed25519.PublicKey)) {
|
|
if setPublicKey == nil {
|
|
log.Error().Msg("license public key init skipped: setPublicKey callback is nil")
|
|
return
|
|
}
|
|
|
|
// Environment override is only allowed in non-release builds.
|
|
if allowPublicKeyEnvOverride() {
|
|
if envKey := os.Getenv("PULSE_LICENSE_PUBLIC_KEY"); envKey != "" {
|
|
key, err := DecodePublicKey(envKey)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("failed to decode PULSE_LICENSE_PUBLIC_KEY, trying embedded key")
|
|
// Fall through to try embedded key instead of returning
|
|
} else {
|
|
setPublicKey(key)
|
|
log.Info().
|
|
Str("source", "environment").
|
|
Str("fingerprint", PublicKeyFingerprint(key)).
|
|
Msg("license public key loaded")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// Embedded key (set at compile time via -ldflags)
|
|
if embeddedPublicKey != "" {
|
|
key, err := DecodePublicKey(embeddedPublicKey)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("failed to decode embedded public key")
|
|
} else {
|
|
setPublicKey(key)
|
|
log.Info().
|
|
Str("source", "embedded").
|
|
Str("fingerprint", PublicKeyFingerprint(key)).
|
|
Msg("license public key loaded")
|
|
return
|
|
}
|
|
}
|
|
|
|
// No key available
|
|
if devMode {
|
|
log.Warn().Msg("License validation running in DEV MODE - signatures not verified")
|
|
} else {
|
|
log.Warn().Msg("no license public key configured - license activation will fail")
|
|
}
|
|
}
|
|
|
|
// DecodePublicKey decodes a base64-encoded Ed25519 public key.
|
|
func DecodePublicKey(encoded string) (ed25519.PublicKey, error) {
|
|
// Remove any whitespace
|
|
encoded = strings.TrimSpace(encoded)
|
|
|
|
// Try standard base64 first, then URL-safe
|
|
decoded, err := base64.StdEncoding.DecodeString(encoded)
|
|
if err != nil {
|
|
decoded, err = base64.RawURLEncoding.DecodeString(encoded)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if len(decoded) != ed25519.PublicKeySize {
|
|
return nil, ErrMalformedPublicKey
|
|
}
|
|
|
|
return ed25519.PublicKey(decoded), nil
|
|
}
|