Simple licensing library for golang.

Overview

license-key

Build Status GoDoc Go Report Card cover.run go

A simple licensing library in Golang, that generates license files containing arbitrary data.

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 Base 64 string and send it to the end user.
  5. when the user starts your program verify the signature using a public key.

lkgen

A command line helper lkgen is also provided to generate private keys and create licenses. Install it with the following command :

go install github.com/hyperboloide/lk/lkgen

See the usage bellow on how to use it (or enter lkgen --help-long):

usage: lkgen [<flags>] <command> [<args> ...]

A command-line utility to generate private keys and licenses.

Flags:
  --help  Show context-sensitive help (also try --help-long and --help-man).

Commands:
  help [<command>...]
    Show help.


  gen [<flags>]
    Generates a base32 encoded private key.

    -c, --curve=p384     Elliptic curve to use.
    -o, --output=OUTPUT  Output file (if not defined then stdout).

  pub [<flags>] <key>
    Get the public key.

    -o, --output=OUTPUT  Output file (if not defined then stdout).

  sign [<flags>] <key>
    Creates a license.

    -i, --input=INPUT    Input data file (if not defined then stdin).
    -o, --output=OUTPUT  Output file (if not defined then stdout).

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.

// 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!
const privateKeyBase32 = "FD7YCAYBAEFXA22DN5XHIYLJNZSXEAP7QIAACAQBANIHKYQBBIAACAKEAH7YIAAAAAFP7AYFAEBP7BQAAAAP7GP7QIAWCBCRKQVWKPT7UJDNP4LB5TXEQMO7EYEGDCE42KVBDNEGRIYIIJFBIWIVB6T6ZTKLSYSGK54DZ5VX6M5SJHBYZU2JXUFXJI25L2JJKJW4RL7UL2XBDT4GKYZ5IS6IWBCN7CWTMVBCBHJMH3RHZ5BVGVAY66MQAEYQEPSS2ANTYZIWXWSGIUJW3MDOO335JK3D4N3IV4L5UTAQMLS5YC7QASCAAUOHTZ5ZCCCYIBNCWBELBMAA===="

// 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"`
}{
	"[email protected]",
	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)

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{
	"[email protected]",
	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"))
}
Issues
  • Retrieving data from license

    Retrieving data from license

    I am using this debugging code to test creating & importing a license.

    licenseRead, openErr := ioutil.ReadFile("./license.pem")
    if openErr == nil {
    	license,_ = lk.LicenseFromB64String(string(licenseRead))
    	fmt.Println(string(licenseRead), license.Data)
    	
    	if ok, err := license.Verify(licensePubKey); err != nil {
    		fmt.Println(err)
    	} else if ok {
    		fmt.Println("Valid license")
    		if jsDecErr := json.Unmarshal(license.Data, &licenseData); jsDecErr != nil {
    			fmt.Println("unmarshal error", jsDecErr)
    		}
    		fmt.Println("unmarshal success", string(license.Data), licenseData)
    	} else {
    		fmt.Println("Invalid license")
    	}
    } else {
    	licenseData = &licenseStruct{os.Args[1], time.Now().Add(time.Hour * 24 * time.Duration(runtime)), runtime}
    	licenseBytes,_ := json.Marshal(licenseData)
    	license,_ = lk.NewLicense(privateKey, licenseBytes)
    	exportLicense,_ := license.ToB64String()
    	
    	ioutil.WriteFile("./license.pem", []byte(exportLicense), 0644)
    	fmt.Println("License generated & saved")
    }
    fmt.Println("Your license data", licenseData)
    

    While this works for the generation of the license (outputs the struct correctly in that instance), the struct is always empty when trying to read the license back in. No function returns any errors and I verified that the raw data of the license on export & import is identical. There is no documentation on how to get the data out of the license again, but according to the source files it's supposed to be the .Data attribute.

    Log of generation

    License generated & saved Your license data &{[email protected] {63647040171 98409900 0x7df480} 100}

    License file

    LP+BAwEBB0xpY2Vuc2UB/4IAAQMBBERhdGEBCgABAVIB/4QAAQFTAf+EAAAACv+DBQEC/4YAAABt/4IBAnt9ATECSXUDye1UQejYyqP9iwSnkSxOGDgpM2fJuGLFHkemhbPtTXA6unPA1fbhn4H+WulmATEC9Do4X225WMaX7QiEvotT76Uel1AoArlJmvs8BybBi5330A0kKGpBPTS8wJW6bz/6AA==

    Log of Import

    LP+BAwEBB0xpY2Vuc2UB/4IAAQMBBERhdGEBCgABAVIB/4QAAQFTAf+EAAAACv+DBQEC/4YAAABt/4IBAnt9ATECSXUDye1UQejYyqP9iwSnkSxOGDgpM2fJuGLFHkemhbPtTXA6unPA1fbhn4H+WulmATEC9Do4X225WMaX7QiEvotT76Uel1AoArlJmvs8BybBi5330A0kKGpBPTS8wJW6bz/6AA== [123 125] &{ {0 0 } 0} Valid license unmarshal success {} &{ {0 0 } 0} Your license data &{ {0 0 } 0}

    opened by JamesCullum 1
  • Adding hexadecimal support

    Adding hexadecimal support

    A lot of applications I use use hexadecimal encodings for their outputs, I find this package very useful and so I've added the hexadecimal support to its functionality.

    opened by Samyoul 0
Owner
Hyperboloide
Hyperboloide
Library to work with MimeHeaders and another mime types. Library support wildcards and parameters.

Mime header Motivation This library created to help people to parse media type data, like headers, and store and match it. The main features of the li

Anton Ohorodnyk 27 May 1, 2022
An simple, easily extensible and concurrent health-check library for Go services

Healthcheck A simple and extensible RESTful Healthcheck API implementation for Go services. Health provides an http.Handlefunc for use as a healthchec

Ether Labs 221 May 24, 2022
A simple Cron library for go that can execute closures or functions at varying intervals, from once a second to once a year on a specific date and time. Primarily for web applications and long running daemons.

Cron.go This is a simple library to handle scheduled tasks. Tasks can be run in a minimum delay of once a second--for which Cron isn't actually design

Robert K 210 May 4, 2022
bf.go - A dead simple brainfuck interpreter Slow and simple

bf.go - A dead simple brainfuck interpreter Slow and simple. Can execute pretty much all tested Brainfuck scripts. Installation If you have Go install

Chris 0 Oct 15, 2021
GoLang Library for Browser Capabilities Project

Browser Capabilities GoLang Project PHP has get_browser() function which tells what the user's browser is capable of. You can check original documenta

Maksim N. 40 Jun 13, 2022
Type-safe Prometheus metrics builder library for golang

gotoprom A Prometheus metrics builder gotoprom offers an easy to use declarative API with type-safe labels for building and using Prometheus metrics.

Cabify 93 Jun 9, 2022
A Golang library to manipulate strings according to the word parsing rules of the UNIX Bourne shell.

shellwords A Golang library to manipulate strings according to the word parsing rules of the UNIX Bourne shell. Installation go get github.com/Wing924

Wei He 17 Mar 15, 2022
Flow-based and dataflow programming library for Go (golang)

GoFlow - Dataflow and Flow-based programming library for Go (golang) Status of this branch (WIP) Warning: you are currently on v1 branch of GoFlow. v1

Vladimir Sibirov 1.4k Jun 24, 2022
a Go (Golang) MusicBrainz WS2 client library - work in progress

gomusicbrainz a Go (Golang) MusicBrainz WS2 client library - a work in progress. Current state Currently GoMusicBrainz provides methods to perform sea

Michael Wendland 45 May 3, 2022
ghw - Golang HardWare discovery/inspection library

ghw - Golang HardWare discovery/inspection library ghw is a small Golang library providing hardware inspection and discovery for Linux and Windows.

Jay Pipes 1.2k Jun 23, 2022
Golang SSR-first Frontend Library

kyoto Library that brings frontend-like components experience to the server side with native html/template on steroids. Supports any serving basis (ne

Yurii Zinets 506 Jun 30, 2022
GoLang Library for Browser Capabilities Project

Browser Capabilities GoLang Project PHP has get_browser() function which tells what the user's browser is capable of. You can check original documenta

Star Inc. 0 Nov 23, 2021
A WSL Library for Golang.

wsllib-go A WSL Library for Golang. Usage Get this package go get

yuk7 4 Jun 13, 2022
Go-linq - A powerful language integrated query (LINQ) library for Golang

go-linq A powerful language integrated query (LINQ) library for Go. Written in v

Ahmet Alp Balkan 3k Jul 1, 2022
libFFM-gp: Pure Golang implemented library for FM (factorization machines)

libFFM-gp: Pure Golang implemented library for FM (factorization machines)

null 0 Jan 23, 2022
Sa818 - Sa818 UHF/VHF walkie talkie module control library for golang

SA818 golang library for serial control This library written in Go programming l

Suvir Kumar 0 Jan 23, 2022
The Webhooks Listener-Plugin library consists of two component libraries written in GoLang

The Webhooks Listener-Plugin library consists of two component libraries written in GoLang: WebHook Listener Libraries and Plugin (Event Consumer) Libraries.

Temur Yunusov 0 Feb 3, 2022
Simple Golang API to demonstrate file upload to fireabase storage and retrieving url of uploaded file.

go-firebase-storage -Work in progress ??️ Simple Golang API that uses Firebase as its backend to demonstrate various firebase services using Go such a

Victor Kabata 4 Oct 4, 2021
simple bank which is implemented using Golang

Banker The service that we’re going to build is a simple bank. It will provide APIs for the frontend to do following things: Create and manage bank ac

null 1 Nov 15, 2021