56ffca767d
7-day full-access trial, then gated behind an Ed25519-signed license key (internal/license) verified entirely offline — no phone-home dependency. Middleware 402s non-exempt API routes once the trial lapses with no valid key; frontend shows a countdown banner in the last 3 days and a blocking overlay once actually gated, both pointing at the new Settings -> Лицензия tab. tools/gen-license mints keys for the vendor side (never ships the private key).
64 lines
1.8 KiB
Go
64 lines
1.8 KiB
Go
package license
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// testKeyPair generates a fresh Ed25519 pair and signs payload with the
|
|
// resulting private key — Parse itself always verifies against the
|
|
// package's real embedded publicKeyHex, so these helpers exist only to
|
|
// exercise the malformed/tampered paths that don't need a genuine
|
|
// signature to fail correctly.
|
|
func signWithKey(priv ed25519.PrivateKey, p Payload) string {
|
|
payloadJSON, _ := json.Marshal(p)
|
|
payloadB64 := base64.RawURLEncoding.EncodeToString(payloadJSON)
|
|
sig := ed25519.Sign(priv, []byte(payloadB64))
|
|
sigB64 := base64.RawURLEncoding.EncodeToString(sig)
|
|
return payloadB64 + "." + sigB64
|
|
}
|
|
|
|
func TestParseRejectsSignatureFromWrongKey(t *testing.T) {
|
|
_, wrongPriv, _ := ed25519.GenerateKey(nil)
|
|
key := signWithKey(wrongPriv, Payload{Customer: "Someone"})
|
|
|
|
_, err := Parse(key)
|
|
if err != ErrInvalidSignature {
|
|
t.Errorf("Parse() err = %v, want ErrInvalidSignature", err)
|
|
}
|
|
}
|
|
|
|
func TestParseRejectsMalformedInput(t *testing.T) {
|
|
cases := []string{"", "no-dot-here", "not-base64.also-not-base64", "..", "a.b.c"}
|
|
for _, c := range cases {
|
|
if _, err := Parse(c); err == nil {
|
|
t.Errorf("Parse(%q) = nil error, want an error", c)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPayloadValidPerpetualNeverExpires(t *testing.T) {
|
|
p := Payload{Customer: "X", IssuedAt: time.Now()}
|
|
if !p.Valid() {
|
|
t.Error("Valid() = false for a nil-ExpiresAt (perpetual) payload, want true")
|
|
}
|
|
}
|
|
|
|
func TestPayloadValidRespectsExpiry(t *testing.T) {
|
|
past := time.Now().Add(-time.Hour)
|
|
future := time.Now().Add(time.Hour)
|
|
|
|
expired := Payload{ExpiresAt: &past}
|
|
if expired.Valid() {
|
|
t.Error("Valid() = true for an already-expired payload, want false")
|
|
}
|
|
|
|
current := Payload{ExpiresAt: &future}
|
|
if !current.Valid() {
|
|
t.Error("Valid() = false for a not-yet-expired payload, want true")
|
|
}
|
|
}
|