package license import ( "crypto/ed25519" "encoding/base64" "os" "strings" "github.com/rs/zerolog/log" ) // EmbeddedPublicKey is the production Ed25519 public key (base64 encoded). // This should be set at build time via -ldflags or populated with the actual key. // Example: go build -ldflags "-X github.com/rcourtman/pulse-go-rewrite/internal/license.EmbeddedPublicKey=BASE64_KEY" var EmbeddedPublicKey string = "" // InitPublicKey initializes the public key for license validation. // Priority: // 1. PULSE_LICENSE_PUBLIC_KEY environment variable (base64 encoded) // 2. EmbeddedPublicKey (set at compile time via -ldflags) // 3. If PULSE_LICENSE_DEV_MODE=true, skip validation (development only) // // Call this during application startup before any license operations. func InitPublicKey() { // Priority 1: Environment variable 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().Msg("License public key loaded from environment") return } } // Priority 2: Embedded key (set at compile time) if EmbeddedPublicKey != "" { key, err := decodePublicKey(EmbeddedPublicKey) if err != nil { log.Error().Err(err).Msg("Failed to decode embedded public key, license validation disabled") return } SetPublicKey(key) log.Info().Msg("License public key loaded from embedded key") return } // Priority 3: Dev mode - no key needed if os.Getenv("PULSE_LICENSE_DEV_MODE") == "true" { log.Warn().Msg("License validation running in DEV MODE - signatures not verified") return } // No key available - this is a problem in production 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, ErrMalformedLicense } return ed25519.PublicKey(decoded), nil }