gooxml: add support for verifying versions and license

This commit is contained in:
Todd 2017-09-29 19:06:16 -05:00
parent 801388a119
commit 665d3a281d
5 changed files with 249 additions and 0 deletions

185
license.go Normal file
View File

@ -0,0 +1,185 @@
// Copyright 2017 Baliance. All rights reserved.
//
// Use of this source code is governed by the terms of the Affero GNU General
// Public License version 3.0 as published by the Free Software Foundation and
// appearing in the file LICENSE included in the packaging of this file. A
// commercial license can be purchased by contacting sales@baliance.com.
package gooxml
import (
"bytes"
"compress/gzip"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"io"
"log"
"strings"
"time"
)
// OpenSourceLicense is the AGPL open source license. It is installed by default
// if no license has previously been installed.
const OpenSourceLicense = `
H4sIAAAAAAAC_xzJsU7DQAwA0J2vqDynks--y9nZ
2RAMLQubfeegDKRR2kogxL8jkN72vuHZPgImeNli
PZwu973F4fUaOwxwWt5Xu933v86Fqjfnlq1mKWzU
SXzEwCKoqWc0sREtvCYlsdlV2b12CwqyntVk7orZ
eTQRtMhMoonHaDh3DMsYVEir10JzbV3CW1FmyrM4
wQCPn9uy2225rDABI-Ix0ZHTGXH69wYDPC0t1muc
v7aAKf08_AYAAP__z2E3oN8A`
var license *License
const pubKeyHex = `305c300d06092a864886f70d0101010500034b003048024100b87eafb6c07499eb97cc9d3565ecf3168196301907c841addc665086bb3ed8eb12d9da26cafa96450146da8bd0ccf155fcacc686955ef0302fa44aa3ec89417b0203010001`
var pubKey *rsa.PublicKey
func init() {
pubKeyBytes, err := hex.DecodeString(pubKeyHex)
if err != nil {
log.Fatalf("error reading key: %s", err)
}
pkRaw, err := x509.ParsePKIXPublicKey(pubKeyBytes)
if err != nil {
log.Fatalf("error reading key: %s", err)
}
pubKey = pkRaw.(*rsa.PublicKey)
}
// LicenseType is the type of license
//go:generate stringer -type=LicenseType
type LicenseType byte
// LicenseType constants
const (
LicenseTypeInvalid LicenseType = iota
LicenseTypeAGPL
LicenseTypeCommercial
)
// License holds the gooxml license information.
type License struct {
Name string
Signature string `json:",omitempty"`
Expiration time.Time
LicenseType LicenseType
}
// Sign signs a license with a private key, setting the license's signature
// value
func (l *License) Sign(privKey *rsa.PrivateKey) error {
l.Signature = ""
buf := bytes.Buffer{}
enc := json.NewEncoder(&buf)
if err := enc.Encode(l); err != nil {
return err
}
hashed := sha256.Sum256(buf.Bytes())
signature, err := rsa.SignPKCS1v15(rand.Reader, privKey, crypto.SHA256, hashed[:])
if err != nil {
return err
}
l.Signature = hex.EncodeToString(signature)
return nil
}
// Verify verifies a license by checking the license content and signature
// against a public key.
func (l License) Verify(pubKey *rsa.PublicKey) error {
cp := l
cp.Signature = ""
buf := bytes.Buffer{}
enc := json.NewEncoder(&buf)
if err := enc.Encode(cp); err != nil {
return err
}
sig, err := hex.DecodeString(l.Signature)
if err != nil {
return err
}
hashed := sha256.Sum256(buf.Bytes())
err = rsa.VerifyPKCS1v15(pubKey, crypto.SHA256, hashed[:], sig)
return err
}
func (l License) String() string {
buf := bytes.Buffer{}
enc := json.NewEncoder(&buf)
enc.Encode(l)
return buf.String()
}
// Encoded returns a base64 encoded version of the license for use with
// InstallLicense.
func (l License) Encoded() string {
buf := bytes.Buffer{}
w := base64.NewEncoder(base64.RawURLEncoding, &buf)
gz, _ := gzip.NewWriterLevel(w, gzip.BestCompression)
enc := json.NewEncoder(gz)
enc.Encode(l)
gz.Close()
rsp := bytes.Buffer{}
const maxLen = 40
raw := buf.Bytes()
for i := 0; i < buf.Len(); i += maxLen {
rsp.Write(raw[i : i+maxLen])
rsp.WriteByte('\r')
rsp.WriteByte('\n')
}
return rsp.String()
}
// InstallLicense installs a license, returning an error if the license is
// invalid or expired. Expiration checks the ReleaseDate variable in version.go.
func InstallLicense(s string) error {
s = strings.Replace(s, "\r", "", -1)
s = strings.Replace(s, "\n", "", -1)
var r io.Reader
r = strings.NewReader(s)
r = base64.NewDecoder(base64.RawURLEncoding, r)
r, err := gzip.NewReader(r)
if err != nil {
return err
}
dec := json.NewDecoder(r)
l := &License{}
if err := dec.Decode(l); err != nil {
return err
}
// check signature
if err := l.Verify(pubKey); err != nil {
return errors.New("license validatin error")
}
if l.Expiration.Before(ReleaseDate) {
return errors.New("license expired")
}
license = l
return nil
}
// GetLicense returns the current license. This can be used by commercial
// customers to assist in ensuring that their license hasn't expired.
func GetLicense() License {
if license == nil {
if err := InstallLicense(OpenSourceLicense); err != nil {
log.Printf("open source license error: %s", err)
}
}
if license != nil {
return *license
}
return License{}
}

15
license_test.go Normal file
View File

@ -0,0 +1,15 @@
// Copyright 2017 Baliance. All rights reserved.
//
// Use of this source code is governed by the terms of the Affero GNU General
// Public License version 3.0 as published by the Free Software Foundation and
// appearing in the file LICENSE included in the packaging of this file. A
// commercial license can be purchased by contacting sales@baliance.com.
package gooxml_test
import "testing"
import "baliance.com/gooxml"
func TestOpenSourceLicense(t *testing.T) {
gooxml.GetLicense()
}

16
licensetype_string.go Normal file
View File

@ -0,0 +1,16 @@
// Code generated by "stringer -type=LicenseType"; DO NOT EDIT.
package gooxml
import "fmt"
const _LicenseType_name = "LicenseTypeInvalidLicenseTypeAGPLLicenseTypeCommercial"
var _LicenseType_index = [...]uint8{0, 18, 33, 54}
func (i LicenseType) String() string {
if i >= LicenseType(len(_LicenseType_index)-1) {
return fmt.Sprintf("LicenseType(%d)", i)
}
return _LicenseType_name[_LicenseType_index[i]:_LicenseType_index[i+1]]
}

20
update-version.sh Executable file
View File

@ -0,0 +1,20 @@
#!/bin/bash
cat << __EOF > version.go
// Copyright `date +%Y` Baliance. All rights reserved.
//
// Use of this source code is governed by the terms of the Affero GNU General
// Public License version 3.0 as published by the Free Software Foundation and
// appearing in the file LICENSE included in the packaging of this file. A
// commercial license can be purchased by contacting sales@baliance.com.
package gooxml
import "time"
// ReleaseDate is the release date of the source code for licensing purposes.
var ReleaseDate = time.Date(`date +%Y`,`date +%_m`,`date +%_d`,0,0,0,0,time.UTC)
__EOF
goimports -w version.go

13
version.go Normal file
View File

@ -0,0 +1,13 @@
// Copyright 2017 Baliance. All rights reserved.
//
// Use of this source code is governed by the terms of the Affero GNU General
// Public License version 3.0 as published by the Free Software Foundation and
// appearing in the file LICENSE included in the packaging of this file. A
// commercial license can be purchased by contacting sales@baliance.com.
package gooxml
import "time"
// ReleaseDate is the release date of the source code for licensing purposes.
var ReleaseDate = time.Date(2017, 9, 30, 0, 0, 0, 0, time.UTC)