You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
92 lines
2.5 KiB
Go
92 lines
2.5 KiB
Go
// Copyright 2018 Drone.IO Inc. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package license
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"encoding/pem"
|
|
"errors"
|
|
"io/ioutil"
|
|
"time"
|
|
|
|
"golang.org/x/crypto/ed25519"
|
|
)
|
|
|
|
var (
|
|
// ErrInvalidSignature indicates the license signature is invalid.
|
|
ErrInvalidSignature = errors.New("Invalid signature")
|
|
|
|
// ErrMalformedLicense indicates the license file is malformed.
|
|
ErrMalformedLicense = errors.New("Malformed License")
|
|
)
|
|
|
|
// License defines a software license key.
|
|
type License struct {
|
|
Iss string `json:"iss,omitempty"` // Issued By
|
|
Cus string `json:"cus,omitempty"` // Customer ID
|
|
Sub string `json:"sub,omitempty"` // Subscriber ID
|
|
Typ string `json:"typ,omitempty"` // License Type
|
|
Lim int `json:"lim,omitempty"` // License Limit (e.g. Seats)
|
|
Iat time.Time `json:"iat,omitempty"` // Issued At
|
|
Exp time.Time `json:"exp,omitempty"` // Expires At
|
|
Dat json.RawMessage `json:"dat,omitempty"` // Metadata
|
|
}
|
|
|
|
// Expired returns true if the license is expired.
|
|
func (l *License) Expired() bool {
|
|
return l.Exp.IsZero() == false && time.Now().After(l.Exp)
|
|
}
|
|
|
|
// Encode generates returns a PEM encoded license key that is
|
|
// signed with the ed25519 private key.
|
|
func (l *License) Encode(privateKey ed25519.PrivateKey) ([]byte, error) {
|
|
msg, err := json.Marshal(l)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sig := ed25519.Sign(privateKey, msg)
|
|
buf := new(bytes.Buffer)
|
|
buf.Write(sig)
|
|
buf.Write(msg)
|
|
|
|
block := &pem.Block{
|
|
Type: "LICENSE KEY",
|
|
Bytes: buf.Bytes(),
|
|
}
|
|
return pem.EncodeToMemory(block), nil
|
|
}
|
|
|
|
// Decode decodes the PEM encoded license key and verifies
|
|
// the content signature using the ed25519 public key.
|
|
func Decode(data []byte, publicKey ed25519.PublicKey) (*License, error) {
|
|
block, _ := pem.Decode(data)
|
|
if block == nil || len(block.Bytes) < ed25519.SignatureSize {
|
|
return nil, ErrMalformedLicense
|
|
}
|
|
|
|
sig := block.Bytes[:ed25519.SignatureSize]
|
|
msg := block.Bytes[ed25519.SignatureSize:]
|
|
|
|
verified := ed25519.Verify(publicKey, msg, sig)
|
|
if !verified {
|
|
return nil, ErrInvalidSignature
|
|
}
|
|
out := new(License)
|
|
err := json.Unmarshal(msg, out)
|
|
return out, err
|
|
}
|
|
|
|
// DecodeFile decodes the PEM encoded license file and verifies
|
|
// the content signature using the ed25519 public key.
|
|
func DecodeFile(path string, publicKey ed25519.PublicKey) (*License, error) {
|
|
data, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return Decode([]byte(data), publicKey)
|
|
}
|