lk

package module
v0.0.0-...-ce3fecd Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 25, 2023 License: MIT Imports: 11 Imported by: 17

README

license-key

Tests Go Reference Go Report Card

A simple licensing library in Golang, that generates license files containing arbitrary data (ex: user email, end date...) that you can further validate if you want.

The license files are signed with the: Elliptic Curve Digital Signature Algorithm (using elliptic.P384 )

The license file can be marshalled in an easy to distribute format (ex: base32 encoded strings)

Note that this implementation is quite basic and that in no way it could prevent someone to hack your software. The goal of this project is only to provide a convenient way for software publishers to generate license keys and distribute them without too much hassle for the user.

How does it works?
  1. Generate a private key (and keep it secure).
  2. Transform the data you want to provide (end date, user email...) to a byte array (using json or gob for example).
  3. The library takes the data and create a cryptographically signed hash that is appended to the data.
  4. Convert the result to a Base64/Base32/Hex string and send it to the end user: this is the license.
  5. when the user starts your program load the license and verify the signature using a public key.
  6. validate the data in your license key (ex: the end date)
lkgen

A command line helper lkgen is also provided to generate private keys and create licenses.

This is also a good example of how to use the library.

Examples
Generating a new license:

Below is an example of code that generates a license from a private key and a struct containing the end date and a user email that is marshalled to json.

// first, you need a base32 encoded private key generated by `lkgen gen` note that you might
// prefer reading it from a file, and that it should stay secret (ie: dont distribute it with your app)!
const privateKeyBase32 = "FD7YCAYBAEFXA22DN5XHIYLJNZSXEAP7QIAACAQBANIHKYQBBIAACAKEAH7YIAAAAAFP7AYFAEBP7BQAAAAP7GP7QIAWCBCRKQVWKPT7UJDNP4LB5TXEQMO7EYEGDCE42KVBDNEGRIYIIJFBIWIVB6T6ZTKLSYSGK54DZ5VX6M5SJHBYZU2JXUFXJI25L2JJKJW4RL7UL2XBDT4GKYZ5IS6IWBCN7CWTMVBCBHJMH3RHZ5BVGVAY66MQAEYQEPSS2ANTYZIWXWSGIUJW3MDOO335JK3D4N3IV4L5UTAQMLS5YC7QASCAAUOHTZ5ZCCCYIBNCWBELBMAA===="

// Unmarshal the private key
privateKey, err := lk.PrivateKeyFromB32String(privateKeyBase32)
if err != nil {
	log.Fatal(err)
}

// Define the data you need in your license,
// here we use a struct that is marshalled to json, but ultimately all you need is a []byte.
doc := struct {
	Email string    `json:"email"`
	End   time.Time `json:"end"`
}{
	"user@example.com",
	time.Now().Add(time.Hour * 24 * 365), // 1 year
}

// marshall the document to []bytes (this is the data that our license will contain).
docBytes, err := json.Marshal(doc)
if err != nil {
	log.Fatal(err)
}

// generate your license with the private key and the document
license, err := lk.NewLicense(privateKey, docBytes)
if err != nil {
	log.Fatal(err)

}
// the b32 representation of our license, this is what you give to your customer.
licenseB32, err := license.ToB32String()
if err != nil {
	log.Fatal(err)
}
fmt.Println(licenseB32)
Validating a license:

Before your execute your program you want to check the user license, here is how with key generated from the previous example:

// A previously generated license b32 encoded. In real life you should read it from a file...
const licenseB32 = "FT7YOAYBAEDUY2LDMVXHGZIB76EAAAIDAECEIYLUMEAQUAABAFJAD74EAAAQCUYB76CAAAAABL7YGBIBAL7YMAAAAD73H74IAFEHWITFNVQWS3BCHIRHIZLTORAGK6DBNVYGYZJOMNXW2IRMEJSW4ZBCHIRDEMBRHAWTCMBNGI3FIMJSHIYTSORTGMXDOMBZG43TIMJYHAVTAMR2GAYCE7IBGEBAPXB37ROJCUOYBVG4LAL3MSNKJKPGIKNT564PYK5X542NH62V7TAUEYHGLEOPZHRBAPH7M4SC55OHAEYQEXMKGG3JPO6BSHTDF3T5H6T42VUD7YAJ3TY5AP5MDE5QW4ZYWMSAPEK24HZOUXQ3LJ5YY34XYPVXBUAA===="

// the public key b32 encoded from the private key using: lkgen pub my_private_key_file`.
// It should be hardcoded somewhere in your app.
const publicKeyBase32 = "ARIVIK3FHZ72ERWX6FQ6Z3SIGHPSMCDBRCONFKQRWSDIUMEEESQULEKQ7J7MZVFZMJDFO6B46237GOZETQ4M2NE32C3UUNOV5EUVE3OIV72F5LQRZ6DFMM6UJPELARG7RLJWKQRATUWD5YT46Q2TKQMPPGIA===="

// Unmarshal the public key.
publicKey, err := lk.PublicKeyFromB32String(publicKeyBase32)
if err != nil {
	log.Fatal(err)
}

// Unmarshal the customer license.
license, err := lk.LicenseFromB32String(licenseB32)
if err != nil {
	log.Fatal(err)
}

// validate the license signature.
if ok, err := license.Verify(publicKey); err != nil {
	log.Fatal(err)
} else if !ok {
	log.Fatal("Invalid license signature")
}

result := struct {
	Email string    `json:"email"`
	End   time.Time `json:"end"`
}{}

// unmarshal the document.
if err := json.Unmarshal(license.Data, &result); err != nil {
	log.Fatal(err)
}

// Now you just have to check that the end date is after time.Now() then you can continue!
if result.End.Before(time.Now()) {
	log.Fatalf("License expired on: %s", result.End.Format("2006-01-02"))
} else {
	fmt.Printf(`Licensed to %s until %s`, result.Email, result.End.Format("2006-01-02"))
}
A Complete example

Bellow is a sample function that generate a key pair, signs a license and verify it.

// create a new Private key:
privateKey, err := lk.NewPrivateKey()
if err != nil {
	log.Fatal(err)

}

// create a license document:
doc := MyLicence{
	"test@example.com",
	time.Now().Add(time.Hour * 24 * 365), // 1 year
}

// marshall the document to json bytes:
docBytes, err := json.Marshal(doc)
if err != nil {
	log.Fatal(err)

}

// generate your license with the private key and the document:
license, err := lk.NewLicense(privateKey, docBytes)
if err != nil {
	log.Fatal(err)

}

// encode the new license to b64, this is what you give to your customer.
str64, err := license.ToB64String()
if err != nil {
	log.Fatal(err)

}
fmt.Println(str64)

// get the public key. The public key should be hardcoded in your app to check licences.
// Do not distribute the private key!
publicKey := privateKey.GetPublicKey()

// validate the license:
if ok, err := license.Verify(publicKey); err != nil {
	log.Fatal(err)
} else if !ok {
	log.Fatal("Invalid license signature")
}

// unmarshal the document and check the end date:
res := MyLicence{}
if err := json.Unmarshal(license.Data, &res); err != nil {
	log.Fatal(err)
} else if res.End.Before(time.Now()) {
	log.Fatalf("License expired on: %s", res.End.String())
} else {
	fmt.Printf(`Licensed to %s until %s \n`, res.Email, res.End.Format("2006-01-02"))
}

Documentation

Overview

Example (Complete)

Example_complete creates a new license and validate it.

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"time"

	"github.com/hyperboloide/lk"
)

type MyLicence struct {
	Email string    `json:"email"`
	End   time.Time `json:"end"`
}

func main() {
	// create a new Private key:
	privateKey, err := lk.NewPrivateKey()
	if err != nil {
		log.Fatal(err)

	}

	// create a license document:
	doc := MyLicence{
		"test@example.com",
		time.Now().Add(time.Hour * 24 * 365), // 1 year
	}

	// marshall the document to json bytes:
	docBytes, err := json.Marshal(doc)
	if err != nil {
		log.Fatal(err)

	}

	// generate your license with the private key and the document:
	license, err := lk.NewLicense(privateKey, docBytes)
	if err != nil {
		log.Fatal(err)

	}

	// encode the new license to b64, this is what you give to your customer.
	str64, err := license.ToB64String()
	if err != nil {
		log.Fatal(err)

	}
	fmt.Println(str64)

	// get the public key. The public key should be hardcoded in your app
	// to check licences. Do not distribute the private key!
	publicKey := privateKey.GetPublicKey()

	// validate the license:
	if ok, err := license.Verify(publicKey); err != nil {
		log.Fatal(err)
	} else if !ok {
		log.Fatal("Invalid license signature")
	}

	// unmarshal the document and check the end date:
	res := MyLicence{}
	if err := json.Unmarshal(license.Data, &res); err != nil {
		log.Fatal(err)
	} else if res.End.Before(time.Now()) {
		log.Fatalf("License expired on: %s", res.End.String())
	} else {
		fmt.Printf(`Licensed to %s until %s \n`, res.Email, res.End.Format("2006-01-02"))
	}
}
Output:

Example (LicenseGeneration)

Example_licenseGeneration shows how to create a license file from a private key.

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"time"

	"github.com/hyperboloide/lk"
)

func main() {

	// a base32 encoded private key generated by `lkgen gen`
	// note that you might prefer reading it from a file...
	const privateKeyBase32 = "FD7YCAYBAEFXA22DN5XHIYLJNZSXEAP7QIAACAQBANIHKYQBBIAACAKEAH7YIAAAAAFP7AYFAEBP7BQAAAAP7GP7QIAWCBCRKQVWKPT7UJDNP4LB5TXEQMO7EYEGDCE42KVBDNEGRIYIIJFBIWIVB6T6ZTKLSYSGK54DZ5VX6M5SJHBYZU2JXUFXJI25L2JJKJW4RL7UL2XBDT4GKYZ5IS6IWBCN7CWTMVBCBHJMH3RHZ5BVGVAY66MQAEYQEPSS2ANTYZIWXWSGIUJW3MDOO335JK3D4N3IV4L5UTAQMLS5YC7QASCAAUOHTZ5ZCCCYIBNCWBELBMAA===="

	// Here we use a struct that is marshalled to json,
	// but ultimatly all you need is a []byte.
	doc := struct {
		Email string    `json:"email"`
		End   time.Time `json:"end"`
	}{
		"test@example.com",
		time.Now().Add(time.Hour * 24 * 365), // 1 year
	}

	// marshall the document to []bytes (this is the data that our license
	// will contain):
	docBytes, err := json.Marshal(doc)
	if err != nil {
		log.Fatal(err)
	}

	// Unmarshal the private key:
	privateKey, err := lk.PrivateKeyFromB32String(privateKeyBase32)
	if err != nil {
		log.Fatal(err)
	}

	// generate your license with the private key and the document:
	license, err := lk.NewLicense(privateKey, docBytes)
	if err != nil {
		log.Fatal(err)

	}
	// the b32 representation of our license, this is what you give to
	// your customer.
	licenseB32, err := license.ToB32String()
	if err != nil {
		log.Fatal(err)

	}
	fmt.Println(licenseB32)
}
Output:

Example (LicenseVerification)

Example_licenseVerification validates a previously generated license with a public key.

package main

import (
	"encoding/json"
	"log"
	"time"

	"github.com/hyperboloide/lk"
)

func main() {

	// A previously generated licence b32 encoded. In real life you should read
	// it from a file at the beginning of your program and check it
	// before doing anything else...
	const licenseB32 = "FT7YOAYBAEDUY2LDMVXHGZIB76EAAAIDAECEIYLUMEAQUAABAFJAD74EAAAQCUYB76CAAAAABL7YGBIBAL7YMAAAAD73H74IAFEHWITFNVQWS3BCHIRHIZLTORAGK6DBNVYGYZJOMNXW2IRMEJSW4ZBCHIRDEMBRHAWTCMBNGI3FIMJSHIYTSORTGMXDOMBZG43TIMJYHAVTAMR2GAYCE7IBGEBAPXB37ROJCUOYBVG4LAL3MSNKJKPGIKNT564PYK5X542NH62V7TAUEYHGLEOPZHRBAPH7M4SC55OHAEYQEXMKGG3JPO6BSHTDF3T5H6T42VUD7YAJ3TY5AP5MDE5QW4ZYWMSAPEK24HZOUXQ3LJ5YY34XYPVXBUAA===="

	// the public key b32 encoded from the private key using:
	// `lkgen pub my_private_key_file`. It should be
	// hardcoded somewhere in your app.
	const publicKeyBase32 = "ARIVIK3FHZ72ERWX6FQ6Z3SIGHPSMCDBRCONFKQRWSDIUMEEESQULEKQ7J7MZVFZMJDFO6B46237GOZETQ4M2NE32C3UUNOV5EUVE3OIV72F5LQRZ6DFMM6UJPELARG7RLJWKQRATUWD5YT46Q2TKQMPPGIA===="

	// Unmarshal the public key
	publicKey, err := lk.PublicKeyFromB32String(publicKeyBase32)
	if err != nil {
		log.Fatal(err)
	}

	// Unmarshal the customer license:
	license, err := lk.LicenseFromB32String(licenseB32)
	if err != nil {
		log.Fatal(err)
	}

	// validate the license signature:
	if ok, err := license.Verify(publicKey); err != nil {
		log.Fatal(err)
	} else if !ok {
		log.Fatal("Invalid license signature")
	}

	result := struct {
		Email string    `json:"email"`
		End   time.Time `json:"end"`
	}{}

	// unmarshal the document:
	if err := json.Unmarshal(license.Data, &result); err != nil {
		log.Fatal(err)
	}

	// Now you just have to check the end date and if it before time.Now(),
	// then you can continue!
	// if result.End.Before(time.Now()) {
	// 	log.Fatal("License expired on: %s", result.End.Format("2006-01-02"))
	// } else {
	// 	fmt.Printf(`Licensed to %s until %s`, result.Email, result.End.Format("2006-01-02"))
	// }

}
Output:

Index

Examples

Constants

This section is empty.

Variables

View Source
var Curve = elliptic.P384

Curve is the elliptic.Curve to use. Default is elliptic.P384.

Functions

This section is empty.

Types

type License

type License struct {
	Data []byte
	R    *big.Int
	S    *big.Int
}

License represents a license with some data and a hash.

func LicenseFromB32String

func LicenseFromB32String(str string) (*License, error)

LicenseFromB32String returns a License from a base64 encoded string.

func LicenseFromB64String

func LicenseFromB64String(str string) (*License, error)

LicenseFromB64String returns a License from a base64 encoded string.

func LicenseFromBytes

func LicenseFromBytes(b []byte) (*License, error)

LicenseFromBytes returns a License from a []byte.

func LicenseFromHexString

func LicenseFromHexString(str string) (*License, error)

LicenseFromHexString returns a License from a hexadecimal encoded string.

func NewLicense

func NewLicense(k *PrivateKey, data []byte) (*License, error)

NewLicense create a new license and sign it.

func (*License) ToB32String

func (l *License) ToB32String() (string, error)

ToB32String transforms the license to a base32 []byte.

func (*License) ToB64String

func (l *License) ToB64String() (string, error)

ToB64String transforms the licence to a base64 []byte.

func (*License) ToBytes

func (l *License) ToBytes() ([]byte, error)

ToBytes transforms the licence to a base64 []byte.

func (*License) ToHexString

func (l *License) ToHexString() (string, error)

ToHexString transforms the license to a hexadecimal []byte.

func (*License) Verify

func (l *License) Verify(k *PublicKey) (bool, error)

Verify the License with the public key

type PrivateKey

type PrivateKey ecdsa.PrivateKey

PrivateKey is the master key to create the licenses. Keep it in a secure location.

func NewPrivateKey

func NewPrivateKey() (*PrivateKey, error)

NewPrivateKey generates a new private key. The default elliptic.Curve used is elliptic.P384().

func PrivateKeyFromB32String

func PrivateKeyFromB32String(str string) (*PrivateKey, error)

PrivateKeyFromB32String returns a private key from a base32 encoded string.

func PrivateKeyFromB64String

func PrivateKeyFromB64String(str string) (*PrivateKey, error)

PrivateKeyFromB64String returns a private key from a base64 encoded string.

func PrivateKeyFromBytes

func PrivateKeyFromBytes(b []byte) (*PrivateKey, error)

PrivateKeyFromBytes returns a private key from a []byte.

func PrivateKeyFromHexString

func PrivateKeyFromHexString(str string) (*PrivateKey, error)

PrivateKeyFromHexString returns a private key from a hexadecimal encoded string.

func (PrivateKey) GetPublicKey

func (k PrivateKey) GetPublicKey() *PublicKey

GetPublicKey returns the PublicKey associated with the private key.

func (PrivateKey) ToB32String

func (k PrivateKey) ToB32String() (string, error)

ToB32String transforms the private key to a base32 string.

func (PrivateKey) ToB64String

func (k PrivateKey) ToB64String() (string, error)

ToB64String transforms the private key to a base64 string.

func (PrivateKey) ToBytes

func (k PrivateKey) ToBytes() ([]byte, error)

ToBytes transforms the private key to a []byte.

func (PrivateKey) ToHexString

func (k PrivateKey) ToHexString() (string, error)

ToHexString transforms the private key to a hexadecimal string

type PublicKey

type PublicKey ecdsa.PublicKey

PublicKey is used to check the validity of the licenses. You can share it freely.

func PublicKeyFromB32String

func PublicKeyFromB32String(str string) (*PublicKey, error)

PublicKeyFromB32String returns a public key from a base32 encoded string.

func PublicKeyFromB64String

func PublicKeyFromB64String(str string) (*PublicKey, error)

PublicKeyFromB64String returns a public key from a base64 encoded string.

func PublicKeyFromBytes

func PublicKeyFromBytes(b []byte) (*PublicKey, error)

PublicKeyFromBytes returns a public key from a []byte.

func PublicKeyFromHexString

func PublicKeyFromHexString(str string) (*PublicKey, error)

PublicKeyFromHexString returns a public key from a hexadecimal encoded string.

func (PublicKey) ToB32String

func (k PublicKey) ToB32String() string

ToB32String transforms the public key to a base32 string.

func (PublicKey) ToB64String

func (k PublicKey) ToB64String() string

ToB64String transforms the public key to a base64 string.

func (PublicKey) ToBytes

func (k PublicKey) ToBytes() []byte

ToBytes transforms the public key to a []byte.

func (PublicKey) ToHexString

func (k PublicKey) ToHexString() string

ToHexString transforms the public key to a hexadecimal string.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL