doodle/pkg/license/license.go
Noah Petherbridge dce32ea14b Diverge Free vs. Paid Features
* Free (shareware) versions of the game will not be able to Publish
  Levels (attach custom doodads to the level file) and they will not be
  able to load a level which relies on embedded doodads.
* The UI for the Publish Level window is still available, but clicking
  on the confirm button will just open the Register (License) window.
* When loading a level containing embedded doodads: if some can't load
  because they're embedded and you're using the free version of the
  game, the error message is customized to reflect that.
2021-06-16 22:35:01 -07:00

109 lines
2.6 KiB
Go

// Package license holds functions related to paid product activation.
package license
import (
"crypto/ecdsa"
"crypto/x509"
"encoding/pem"
"errors"
"io/ioutil"
"path/filepath"
"git.kirsle.net/apps/doodle/pkg/userdir"
"github.com/dgrijalva/jwt-go"
)
// Errors
var (
ErrRegisteredFeature = errors.New("feature not available")
)
// Registration object encoded into a license key file.
type Registration struct {
Name string `json:"name"`
Email string `json:"email"`
jwt.StandardClaims
}
// IsRegistered returns a boolean answer: is the product registered?
func IsRegistered() bool {
if _, err := GetRegistration(); err == nil {
return true
}
return false
}
// GetRegistration returns the currently registered user, by checking
// for the license.key file in the profile folder.
func GetRegistration() (Registration, error) {
if Signer == nil {
return Registration{}, errors.New("signer not ready")
}
filename := filepath.Join(userdir.ProfileDirectory, "license.key")
jwt, err := ioutil.ReadFile(filename)
if err != nil {
return Registration{}, err
}
// Check if the JWT is valid.
reg, err := Validate(Signer, string(jwt))
if err != nil {
return Registration{}, err
}
return reg, err
}
// UploadLicenseFile handles the user selecting the license key file, and it is
// validated and ingested.
func UploadLicenseFile(filename string) (Registration, error) {
if Signer == nil {
return Registration{}, errors.New("signer not ready")
}
jwt, err := ioutil.ReadFile(filename)
if err != nil {
return Registration{}, err
}
// Check if the JWT is valid.
reg, err := Validate(Signer, string(jwt))
if err != nil {
return Registration{}, err
}
// Upload the license to Doodle's profile directory.
outfile := filepath.Join(userdir.ProfileDirectory, "license.key")
if err := ioutil.WriteFile(outfile, jwt, 0644); err != nil {
return Registration{}, err
}
return reg, nil
}
// Validate the registration is signed by the appropriate public key.
func Validate(publicKey *ecdsa.PublicKey, tokenString string) (Registration, error) {
var reg Registration
token, err := jwt.ParseWithClaims(tokenString, &reg, func(token *jwt.Token) (interface{}, error) {
return publicKey, nil
})
if err != nil {
return reg, err
}
if !token.Valid {
return reg, errors.New("token not valid")
}
return reg, nil
}
// ParsePublicKeyPEM loads a public key from PEM format.
func ParsePublicKeyPEM(keytext string) (*ecdsa.PublicKey, error) {
blockPub, _ := pem.Decode([]byte(keytext))
x509EncodedPub := blockPub.Bytes
genericPublicKey, _ := x509.ParsePKIXPublicKey(x509EncodedPub)
publicKey := genericPublicKey.(*ecdsa.PublicKey)
return publicKey, nil
}