doodle/pkg/license/admin.go
Noah Petherbridge 0449737607 License Key Registration with ECDSA JWT Tokens
* New command-line tool: doodle-admin for signing license keys for
  users. Includes functions to initialize a keypair, sign license keys
  and validate existing keys.
* The Main Menu screen shows a blue "Register Game" button in the bottom
  right corner of the screen, for unregistered users only.
* In Edit Mode, there is a "Help -> Register" menu item that opens the
  License Window.
* The License UI Window lets the user select the license.key file to
  register the game with. If registered, a copy of the key is placed in
  Doodle's profile directory and the licensee name/email is shown in the
  License UI window.
* Unregistered games will show the word "(shareware)" next to the title
  screen version number and Edit Mode status bar.
* No restrictions are yet placed on free versions of the game.
2021-06-16 21:56:30 -07:00

101 lines
2.5 KiB
Go

package license
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"io/ioutil"
"time"
"github.com/dgrijalva/jwt-go"
)
// AdminGenerateKeys generates the ECDSA public and private key pair for the admin
// side of creating signed license files.
func AdminGenerateKeys() (*ecdsa.PrivateKey, error) {
privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
return privateKey, err
}
// AdminWriteKeys writes the admin signing key to .pem files on disk.
func AdminWriteKeys(key *ecdsa.PrivateKey, privateFile, publicFile string) error {
// Encode the private key to PEM format.
x509Encoded, err := x509.MarshalECPrivateKey(key)
if err != nil {
return err
}
pemEncoded := pem.EncodeToMemory(&pem.Block{
Type: "PRIVATE KEY",
Bytes: x509Encoded,
})
// Encode the public key to PEM format.
x509EncodedPub, err := x509.MarshalPKIXPublicKey(key.Public())
if err != nil {
return err
}
pemEncodedPub := pem.EncodeToMemory(&pem.Block{
Type: "PUBLIC KEY",
Bytes: x509EncodedPub,
})
// Write the files.
if err := ioutil.WriteFile(privateFile, pemEncoded, 0600); err != nil {
return err
}
if err := ioutil.WriteFile(publicFile, pemEncodedPub, 0644); err != nil {
return err
}
return nil
}
// AdminLoadPrivateKey loads the private key from disk.
func AdminLoadPrivateKey(privateFile string) (*ecdsa.PrivateKey, error) {
// Read the private key file.
pemEncoded, err := ioutil.ReadFile(privateFile)
if err != nil {
return nil, err
}
// Decode the private key.
block, _ := pem.Decode([]byte(pemEncoded))
x509Encoded := block.Bytes
privateKey, _ := x509.ParseECPrivateKey(x509Encoded)
return privateKey, nil
}
// AdminLoadPublicKey loads the private key from disk.
func AdminLoadPublicKey(publicFile string) (*ecdsa.PublicKey, error) {
pemEncodedPub, err := ioutil.ReadFile(publicFile)
if err != nil {
return nil, err
}
// Decode the public key.
blockPub, _ := pem.Decode([]byte(pemEncodedPub))
x509EncodedPub := blockPub.Bytes
genericPublicKey, _ := x509.ParsePKIXPublicKey(x509EncodedPub)
publicKey := genericPublicKey.(*ecdsa.PublicKey)
return publicKey, nil
}
// AdminSignRegistration signs the registration object.
func AdminSignRegistration(key *ecdsa.PrivateKey, reg Registration) (string, error) {
reg.StandardClaims = jwt.StandardClaims{
Issuer: "Maze Admin",
IssuedAt: time.Now().Unix(),
NotBefore: time.Now().Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodES384, reg)
signed, err := token.SignedString(key)
if err != nil {
return "", err
}
return signed, nil
}