-
Notifications
You must be signed in to change notification settings - Fork 113
/
reader.go
150 lines (125 loc) · 3.66 KB
/
reader.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// package license provides functions for reading and printing
// licenses generated by Chef's license generation service.
//
// WARNING WARNING WARNING WARNING WARNING
//
// The code in this package is a copy of code in the chef/license
// library. chef/license is still proprietary and thus cannot be
// included in full as a dependency. Please take care that any changes
// to this file are compatible with the chef/license library.
//
// WARNING WARNING WARNING WARNING WARNING
//
package license
import (
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"strings"
"time"
jwt "github.com/dgrijalva/jwt-go"
"github.com/golang/protobuf/ptypes/timestamp"
)
type licenseWithClaims struct {
License
jwt.StandardClaims
}
// GetKeySha256 fetches the SHA256 of the public key from the JWT encoded license
func GetKeySha256(jwtLicense string) (string, error) {
var jwtPayload map[string]interface{}
// Current JWT standard is a base64 string separated by 3 periods
jwtParts := strings.Split(jwtLicense, ".")
if len(jwtParts) != 3 {
return "", errors.New("Not Valid JWT String")
}
// Use JWT package base64 decoder
data, err := jwt.DecodeSegment(jwtParts[1])
if err != nil {
return "", err
}
if err := json.Unmarshal(data, &jwtPayload); err != nil {
return "", err
}
if jwtPayload["key_sha256"] == nil {
return "", errors.New("Couldn't find key_sha256 in token's claims")
}
return jwtPayload["key_sha256"].(string), nil
}
// Read and parse a license from a string and return the config
// Ex: sha, _ := license.GetKeySha256(license)
// lic, _ := license.Read(license, publicKeys[sha])
func Read(license string, publicKey []byte) (*License, error) {
block, _ := pem.Decode(publicKey)
if block == nil || block.Type != "PUBLIC KEY" {
return nil, fmt.Errorf("Failed to decode PEM block containing public key")
}
key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("Failed to parse public key: %+v", err)
}
decrypter := func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodECDSA); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return key, nil
}
var lwc = licenseWithClaims{}
token, err := jwt.ParseWithClaims(license, &lwc, decrypter)
if err != nil {
return nil, err
}
if !token.Valid {
return nil, fmt.Errorf("Token is not valid")
}
claims, ok := token.Claims.(*licenseWithClaims)
if !ok {
return nil, fmt.Errorf(
"Couldn't map data fields to the right type. This is an internal error with the license",
)
}
return &claims.License, nil
}
// PrettyPrint a license into a nice string for text output
func (l *License) PrettyPrint() string {
entitlements := make([]string, 0, len(l.Entitlements))
for _, ent := range l.Entitlements {
entf := fmt.Sprintf(`
Name: %s
Measure: %s, Limit: %d
Start: %s, End: %s
`,
ent.Name,
ent.Measure, ent.Limit,
timestampToRFC3339(ent.Start), timestampToRFC3339(ent.End),
)
entitlements = append(entitlements, entf)
}
return fmt.Sprintf(`
License ID: %s
License Type: %s
License Version: v%s
License Generation: %s
Signing key SHA256: %s
Generation date: %s
Customer: %s (ID: %s v%s)
Entitlements:
%s
`,
l.Id,
l.Type,
l.Version,
l.Generator,
l.KeySha256,
timestampToRFC3339(l.GenerationDate),
l.Customer, l.CustomerId, l.CustomerIdVersion,
strings.Join(entitlements, "\n"))
}
// timestampToRFC3339 converts protobuf timestamp to RFC 3339 string
func timestampToRFC3339(t *timestamp.Timestamp) string {
if t == nil {
t = ×tamp.Timestamp{}
}
return time.Unix(t.Seconds, 0).UTC().Format(time.RFC3339)
}