storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/licverifier/verifier.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2020 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // Package licverifier implements a simple library to verify MinIO Subnet license keys. 18 package licverifier 19 20 import ( 21 "crypto/ecdsa" 22 "errors" 23 "fmt" 24 "time" 25 26 "github.com/dgrijalva/jwt-go" 27 ) 28 29 // LicenseVerifier needs an ECDSA public key in PEM format for initialization. 30 type LicenseVerifier struct { 31 ecPubKey *ecdsa.PublicKey 32 } 33 34 // LicenseInfo holds customer metadata present in the license key. 35 type LicenseInfo struct { 36 Email string // Email of the license key requestor 37 Organization string // Subnet organization name 38 AccountID int64 // Subnet account id 39 StorageCapacity int64 // Storage capacity used in TB 40 Plan string // Subnet plan 41 ExpiresAt time.Time // Time of license expiry 42 } 43 44 // license key JSON field names 45 const ( 46 accountID = "aid" 47 sub = "sub" 48 expiresAt = "exp" 49 organization = "org" 50 capacity = "cap" 51 plan = "plan" 52 ) 53 54 // NewLicenseVerifier returns an initialized license verifier with the given 55 // ECDSA public key in PEM format. 56 func NewLicenseVerifier(pemBytes []byte) (*LicenseVerifier, error) { 57 pbKey, err := jwt.ParseECPublicKeyFromPEM(pemBytes) 58 if err != nil { 59 return nil, fmt.Errorf("Failed to parse public key: %s", err) 60 } 61 return &LicenseVerifier{ 62 ecPubKey: pbKey, 63 }, nil 64 } 65 66 // toLicenseInfo extracts LicenseInfo from claims. It returns an error if any of 67 // the claim values are invalid. 68 func toLicenseInfo(claims jwt.MapClaims) (LicenseInfo, error) { 69 accID, ok := claims[accountID].(float64) 70 if !ok || ok && accID <= 0 { 71 return LicenseInfo{}, errors.New("Invalid accountId in claims") 72 } 73 email, ok := claims[sub].(string) 74 if !ok { 75 return LicenseInfo{}, errors.New("Invalid email in claims") 76 } 77 expiryTS, ok := claims[expiresAt].(float64) 78 if !ok { 79 return LicenseInfo{}, errors.New("Invalid time of expiry in claims") 80 } 81 expiresAt := time.Unix(int64(expiryTS), 0) 82 orgName, ok := claims[organization].(string) 83 if !ok { 84 return LicenseInfo{}, errors.New("Invalid organization in claims") 85 } 86 storageCap, ok := claims[capacity].(float64) 87 if !ok { 88 return LicenseInfo{}, errors.New("Invalid storage capacity in claims") 89 } 90 plan, ok := claims[plan].(string) 91 if !ok { 92 return LicenseInfo{}, errors.New("Invalid plan in claims") 93 } 94 return LicenseInfo{ 95 Email: email, 96 Organization: orgName, 97 AccountID: int64(accID), 98 StorageCapacity: int64(storageCap), 99 Plan: plan, 100 ExpiresAt: expiresAt, 101 }, nil 102 103 } 104 105 // Verify verifies the license key and validates the claims present in it. 106 func (lv *LicenseVerifier) Verify(license string) (LicenseInfo, error) { 107 token, err := jwt.ParseWithClaims(license, &jwt.MapClaims{}, func(token *jwt.Token) (interface{}, error) { 108 return lv.ecPubKey, nil 109 }) 110 if err != nil { 111 return LicenseInfo{}, fmt.Errorf("Failed to verify license: %s", err) 112 } 113 if claims, ok := token.Claims.(*jwt.MapClaims); ok && token.Valid { 114 return toLicenseInfo(*claims) 115 } 116 return LicenseInfo{}, errors.New("Invalid claims found in license") 117 }