Documentation ¶
Overview ¶
Package licensefile defines the format for a license key file and tooling for creating, signing, reading, verifying, and interacting with a license key file.
A license key file is a text file storing YAML or JSON encoded data. The data stored in a license key file has a standardized format that can include customized data per a third-party app's (app reading and verifying the license) needs. The Signature field contains a public-private keypair signature of the other publically marshalled fields in the license key file. The signature authenticates the data in the license key file; if any data in the license key file is changed, or the signature is changed, validation will fail.
Creating and Signing a License Key File ¶
The process of creating a license key file and signing it is as follows:
- A File's fields are populated.
- The File is marshalled, using FileFormat, to a byte slice.
- The bytes are hashed.
- The hash is signed via a previously defined private key.
- The generated signature is encoded into a textual format.
- The human readable signature is set to the File's Signature field.
- The File is marshalled, again, but this time with the Signature field populated.
- The output from marshalling is saved to a text file or served to as a browser response.
Reading and Verifying a License Key File ¶
The process of reading and verifying a license key file is as follows:
- A text file is read from the filesystem.
- The read bytes are unmarshalled to a File struct.
- The signature is removed from the File and decoded.
- The File is marshalled and the resulting bytes are hashed.
- The decoded signature is compared against the hash using a public key.
- If the signature is valid, the license key file's data can be used.
- Check that the license isn't expired.
Index ¶
- Constants
- Variables
- func GenerateKeyPair(k KeyPairAlgoType) (private, public []byte, err error)
- func GenerateKeyPairECDSA(k KeyPairAlgoType) (private, public []byte, err error)
- func GenerateKeyPairED25519() (private, public []byte, err error)
- func GenerateKeyPairRSA(k KeyPairAlgoType) (private, public []byte, err error)
- type File
- func (f *File) Expired() (yes bool, err error)
- func (f *File) ExpiresIn() (d time.Duration, err error)
- func (f *File) ExpiresInDays() (days int, err error)
- func (f *File) ExtraAsBool(name string) (b bool, err error)
- func (f *File) ExtraAsFloat(name string) (x float64, err error)
- func (f *File) ExtraAsInt(name string) (i int, err error)
- func (f *File) ExtraAsString(name string) (s string, err error)
- func (f *File) FileFormat() FileFormat
- func (f *File) Marshal() (b []byte, err error)
- func (f *File) SetFileFormat(format FileFormat)
- func (f *File) Sign(privateKey []byte, keyPairAlgo KeyPairAlgoType) (err error)
- func (f *File) SignECDSA(privateKey []byte, keyPairAlgo KeyPairAlgoType) (err error)
- func (f *File) SignED25519(privateKey []byte) (err error)
- func (f *File) SignRSA(privateKey []byte, keyPairAlgo KeyPairAlgoType) (err error)
- func (f *File) Verify(publicKey []byte, keyPairAlgo KeyPairAlgoType) (err error)deprecated
- func (f *File) VerifyECDSA(publicKey []byte, keyPairAlgo KeyPairAlgoType) (err error)deprecated
- func (f *File) VerifyED25519(publicKey []byte) (err error)deprecated
- func (f *File) VerifyRSA(publicKey []byte, keyPairAlgo KeyPairAlgoType) (err error)deprecated
- func (f *File) VerifySignature(publicKey []byte, keyPairAlgo KeyPairAlgoType) (err error)
- func (f File) VerifySignatureECDSA(publicKey []byte, keyPairAlgo KeyPairAlgoType) (err error)
- func (f File) VerifySignatureED25519(publicKey []byte) (err error)
- func (f File) VerifySignatureRSA(publicKey []byte, keyPairAlgo KeyPairAlgoType) (err error)
- func (f *File) Write(out io.Writer) (err error)
- type FileFormat
- type KeyPairAlgoType
Constants ¶
const ( FileFormatYAML = FileFormat("yaml") FileFormatJSON = FileFormat("json") )
const ( KeyPairAlgoECDSAP256 = KeyPairAlgoType("ECDSA (P256)") KeyPairAlgoECDSAP384 = KeyPairAlgoType("ECDSA (P384)") KeyPairAlgoECDSAP521 = KeyPairAlgoType("ECDSA (P521)") KeyPairAlgoRSA2048 = KeyPairAlgoType("RSA (2048-bit)") KeyPairAlgoRSA4096 = KeyPairAlgoType("RSA (4096-bit)") KeyPairAlgoED25519 = KeyPairAlgoType("ED25519") )
Variables ¶
var ( // ErrBadSignature is returned from Verify() or Verify...() when a File's Signature // cannot be verified with the given public key. ErrBadSignature = errors.New("signature invalid") // ErrMissingExpireDate is returned when trying to check if a license is expires // or in how long it expires via the Expired() or ExpiresIn() funcs. This error //should really never be returned since the only time these funcs are used are //with an existing license's data. ErrMissingExpireDate = errors.New("missing expire date") )
Errors when validating a license key file.
var ErrFieldDoesNotExist = errors.New("extra field does not exist")
ErrFieldDoesNotExist is returned when trying to retrieve a field from the Extra map of a File with a given key name using one of the ExtraAs... funcs but the named field does not exist in the map.
Functions ¶
func GenerateKeyPair ¶
func GenerateKeyPair(k KeyPairAlgoType) (private, public []byte, err error)
GenerateKeyPair creates and returns a new private and public key.
func GenerateKeyPairECDSA ¶
func GenerateKeyPairECDSA(k KeyPairAlgoType) (private, public []byte, err error)
GenerateKeyPairECDSA creates and returns a new ECDSA private and public key. We don't just accept an elliptic.Curve as an input because this overall code base does not support every curve type.
func GenerateKeyPairED25519 ¶
GenerateKeyPairED25519 creates and returns a new ED25519 private and public key.
func GenerateKeyPairRSA ¶
func GenerateKeyPairRSA(k KeyPairAlgoType) (private, public []byte, err error)
GenerateKeyPairRSA creates and returns a new RSA private and public key. We don't just accept a bitsize as an input because this overall code base does not support every bit size.
Types ¶
type File ¶
type File struct { //Optionally displayed fields per app. These are at the top of the struct //definition so that they will be displayed at the top of the marshalled data just //for ease of human reading of the license key file. LicenseID int64 `json:"LicenseID,omitempty" yaml:"LicenseID,omitempty"` AppName string `json:"AppName,omitempty" yaml:"AppName,omitempty"` //This data copied from db-license.go and always included in each license key file. CompanyName string `yaml:"CompanyName"` ContactName string `yaml:"ContactName"` PhoneNumber string `yaml:"PhoneNumber"` Email string `yaml:"Email"` IssueDate string `yaml:"IssueDate"` //YYYY-MM-DD IssueTimestamp int64 `yaml:"IssueTimestamp"` //unix timestamp in seconds ExpireDate string `yaml:"ExpireDate"` //YYYY-MM-DD, in UTC timezone for easiest comparison in DaysUntilExpired() //The name and value for each custom field result. This is stored as a key //value pair and we use an interface since custom fields can have many types and //this is just easier. Extras map[string]interface{} `json:"Extras,omitempty" yaml:"Extras,omitempty"` //Signature is the result of signing the hash of File (all of the above fields) //using the private key. The result is stored here and File is output to a text //file known as the complete license key file. This file is distributed to and //imported into your app by the end-user to allow the app's use. Signature string `yaml:"Signature"` // contains filtered or unexported fields }
File defines the format of data stored in a license key file. This is the body of the text file.
Struct tags are needed for YAML since otherwise when marshalling the field names will be converted to lowercase. We want to maintain camel case since that matches the format used when marshalling to JSON.
We use a struct with a map, instead of just map, so that we can more easily interact with common fields and store some non-marshalled license data. More simply, having a struct is just nicer for interacting with.
func Read ¶
func Read(path string, format FileFormat) (f File, err error)
Read reads a license key file from the given path, unmarshals it, and returns it's data as a File. This checks if the file exists and the data is of the correct format.
This DOES NOT check if the license key file itself (the contents of the file and the signature) is valid nor does this check if the license is expired. You should call VerifySignature() and Expired() on the returned File immediately after calling this func.
func Unmarshal ¶
func Unmarshal(in []byte, format FileFormat) (f File, err error)
Unmarshal takes data read from a file, or elsewhere, and deserializes it from the requested file format into a File. This is used when reading a license file for verifying it/the signature. If unmarshalling is successful, the format is saved to the File's FileFormat field. It is typically easier to call Read() instead since it handles reading a file from a path and deserializing it.
func (*File) Expired ¶ added in v2.1.0
Expired returns if a lincense File's expiration date is in the past.
You should only call this AFTER calling VerifySignature() otherwise the expiration date in the File is untrustworthy and could have been modified.
Signature verification and expiration date checking were kept separate on purpose so that each step can be handled more deliberately with specific handling of invalid states (i.e.: for more graceful handling).
func (*File) ExpiresIn ¶ added in v2.1.0
ExpiresIn calculates duration until a license File expires. The returned duration will be negative for an expired license.
You should only call this AFTER calling VerifySignature() otherwise the expiration date in the File is untrustworthy and could have been modified.
func (*File) ExpiresInDays ¶ added in v2.1.0
ExpiresInDays is a wrapper around ExpiresIn that returns the number of days a license File expires in. The returned days will be negative for an expired license.
You should only call this AFTER calling VerifySignature() otherwise the expiration date in the File is untrustworthy and could have been modified.
func (*File) ExtraAsBool ¶
ExtraAsBool returns the value of the Extra field with the given name as a bool. If the field cannot be found, an error is returned. If the field cannot be type asserted to an bool, an error is returned.
func (*File) ExtraAsFloat ¶
ExtraAsFloat returns the value of the Extra field with the given name as a float64. If the field cannot be found, an error is returned. If the field cannot be type asserted to a float64, an error is returned.
func (*File) ExtraAsInt ¶
ExtraAsInt returns the value of the Extra field with the given name as an int64. If the field cannot be found, an error is returned. If the field cannot be type asserted to an int, an error is returned.
func (*File) ExtraAsString ¶
ExtraAsString returns the value of the Extra field with the given name as a string. If the field cannot be found, an error is returned. If the field cannot be type asserted to a string, an error is returned.
func (*File) FileFormat ¶
func (f *File) FileFormat() FileFormat
FileFormat returns a File's fileFormat field. This func is needed since the fileFormat field is not exported since it is not distributed/writted in a license file.
func (*File) SetFileFormat ¶
func (f *File) SetFileFormat(format FileFormat)
SetFileFormat populates the fileFormat field. This func is needed since the fileFormat field is not exported since it is not distributed/written in a license file.
func (*File) Sign ¶
func (f *File) Sign(privateKey []byte, keyPairAlgo KeyPairAlgoType) (err error)
Sign creates a signature for a license file. The signature is set in the provided File's Signature field. The private key must be decrypted, if needed, prior to being provided. The signature will be encoded per the File's EncodingType.
func (*File) SignECDSA ¶
func (f *File) SignECDSA(privateKey []byte, keyPairAlgo KeyPairAlgoType) (err error)
SignECDSA signs File with the provided ECDSA private key. The generated signature will be set in the Signature field of File. You would need to call File.Marshal() after this func completes to return/serve the license key file. The private key must be decrypted, if needed, prior to being provided.
func (*File) SignED25519 ¶
SignED25519 signs File with the provided ED25519 private key. The generated signature will be set in the Signature field of File. You would need to call File.Marshal() after this func completes to return/serve the license key file. The private key must be decrypted, if needed, prior to being provided.
A KeyPairAlgoType is not needed since there is only one version of ED25519 that can be used whereas with ECDSA or RSA there are multiple versions (curve, bitsize).
func (*File) SignRSA ¶
func (f *File) SignRSA(privateKey []byte, keyPairAlgo KeyPairAlgoType) (err error)
SignRSA signs File with the provided RSA private key. The generated signature will be set in the Signature field of File. You would need to call File.Marshal() after this func completes to return/serve the license key file. The private key must be decrypted, if needed, prior to being provided.
func (*File) Verify
deprecated
func (f *File) Verify(publicKey []byte, keyPairAlgo KeyPairAlgoType) (err error)
Verify calls VerifySignature().
Deprecated: This func is here just for legacy situations since the old Verify() func was renamed to VerifySignature() for better clarity. Use VerifySignature() instead.
func (*File) VerifyECDSA
deprecated
func (f *File) VerifyECDSA(publicKey []byte, keyPairAlgo KeyPairAlgoType) (err error)
VerifyECDSA calls VerifySignatureECDSA().
Deprecated: This func is here just for legacy situations since the old VerifyECDSA() func was renamed to VerifySignatureECDSA() for better clarity. Use VerifySignatureECDSA() instead.
func (*File) VerifyED25519
deprecated
func (*File) VerifyRSA
deprecated
func (f *File) VerifyRSA(publicKey []byte, keyPairAlgo KeyPairAlgoType) (err error)
VerifyRSA calls VerifySignatureRSA().
Deprecated: This func is here just for legacy situations since the old VerifyRSA() func was renamed to VerifySignatureRSA() for better clarity. Use VerifySignatureRSA() instead.
func (*File) VerifySignature ¶ added in v2.1.0
func (f *File) VerifySignature(publicKey []byte, keyPairAlgo KeyPairAlgoType) (err error)
VerifySignature checks if a File's signature is valid by checking it against the publicKey.
This DOES NOT check if a File is expired. You should call Expired() on the File after calling this func.
Signature verification and expiration date checking were kept separate on purpose so that each step can be handled more deliberately with specific handling of invalid states (i.e.: for more graceful handling).
func (File) VerifySignatureECDSA ¶ added in v2.1.0
func (f File) VerifySignatureECDSA(publicKey []byte, keyPairAlgo KeyPairAlgoType) (err error)
VerifySignatureECDSA checks if a File's signature is valid by checking it against the ECDSA public key.
This DOES NOT check if a File is expired. You should call Expired() on the File after calling this func.
This uses a copy of the File since need to remove the Signature field prior to hashing and verification but we don't want to modify the original File so it can be used as it was parsed/unmarshalled.
func (File) VerifySignatureED25519 ¶ added in v2.1.0
VerifySignatureED25519 checks if a File's signature is valid by checking it against the ED25519 public key.
This DOES NOT check if a File is expired. You should call Expired() on the File after calling this func.
This uses a copy of the File since need to remove the Signature field prior to hashing and verification but we don't want to modify the original File so it can be used as it was parsed/unmarshalled.
A KeyPairAlgoType is not needed since there is only one version of ED25519 that can be used whereas with ECDSA or RSA there are multiple versions (curve, bitsize).
func (File) VerifySignatureRSA ¶ added in v2.1.0
func (f File) VerifySignatureRSA(publicKey []byte, keyPairAlgo KeyPairAlgoType) (err error)
VerifySignatureRSA checks if the File's signature is valid by checking it against the RSA public key.
This DOES NOT check if a File is expired. You should call Expired() on the File after calling this func.
This uses a copy of the File since need to remove the Signature field prior to hashing and verification but we don't want to modify the original File so it can be used as it was parsed/unmarshalled.
func (*File) Write ¶
Write writes a File to out. This is used to output the complete license key file. This can be used to write the File to a buffer, as is done when creating a license key file, write the File back to the browser as html, or write the File to an actual filesystem file.
For use with a buffer:
//b := bytes.Buffer{} //err := f.Write(&b)
Writing to an http.ResponseWriter:
//func handler(w http.ResponseWriter, r *http.Request) { // //... // err := f.Write(w) //}
type FileFormat ¶
type FileFormat string
FileFormat is the format of the license key file's data.
func (FileFormat) Valid ¶
func (f FileFormat) Valid() error
Valid checks if a provided file format is one of our supported file formats.
type KeyPairAlgoType ¶
type KeyPairAlgoType string
KeyPairAlgoType is the key pair algorithm used to sign and verify a license key file.
func (KeyPairAlgoType) Valid ¶
func (k KeyPairAlgoType) Valid() error
Valid checks if a provided algorithm is one of our supported key pair algorithms.