Files
license/sdk/licence/verify.go
2026-07-31 16:59:55 +08:00

90 lines
2.5 KiB
Go

package licence
import (
"crypto/ed25519"
"fmt"
"time"
)
func Verify(content []byte, expected Subject) (*Licence, error) {
envelope, err := Parse(content)
if err != nil {
return nil, err
}
if err := validateQuotas(envelope.Licence.Quotas); err != nil {
return nil, err
}
publicKey, err := VerifySigningKeyCertificate(envelope.SigningKeyCertificate)
if err != nil {
return nil, err
}
licenceSigningKeyID, err := parseUUID(envelope.Licence.SigningKeyID)
if err != nil {
return nil, fmt.Errorf("%w: 签发密钥 ID 格式错误", ErrMalformedFile)
}
certificateKeyID, err := parseUUID(envelope.SigningKeyCertificate.KeyID)
if err != nil {
return nil, fmt.Errorf("%w: 凭证密钥 ID 格式错误", ErrInvalidKeyCertificate)
}
if licenceSigningKeyID != certificateKeyID {
return nil, fmt.Errorf("%w: 签发密钥 ID 不一致", ErrInvalidKeyCertificate)
}
message, err := ClaimsMessage(envelope.Licence)
if err != nil {
return nil, err
}
signature, err := decodeBase64URL(envelope.Signature, ed25519.SignatureSize)
if err != nil {
return nil, fmt.Errorf("%w: 许可证签名格式错误", ErrMalformedFile)
}
if !ed25519.Verify(publicKey, message, signature) {
return nil, ErrInvalidSignature
}
if envelope.Licence.PlatformName != expected.PlatformName || envelope.Licence.Workspace != expected.Workspace {
return nil, ErrSubjectMismatch
}
return verifiedLicenceForDate(envelope.Licence, time.Now())
}
func verifiedLicenceForDate(claims Claims, now time.Time) (*Licence, error) {
issuedOn, err := claims.IssuedOn.Time()
if err != nil {
return nil, fmt.Errorf("%w: 签发日期格式错误", ErrMalformedFile)
}
validFrom, err := claims.ValidFrom.Time()
if err != nil {
return nil, fmt.Errorf("%w: 生效日期格式错误", ErrMalformedFile)
}
expiresOn, err := claims.ExpiresOn.Time()
if err != nil {
return nil, fmt.Errorf("%w: 到期日期格式错误", ErrMalformedFile)
}
location, err := locationShanghai()
if err != nil {
return nil, fmt.Errorf("%w: 无法加载日期时区", ErrMalformedFile)
}
now = now.In(location)
today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, location)
if today.Before(validFrom) {
return nil, ErrNotYetValid
}
if today.After(expiresOn) {
return nil, ErrExpired
}
return &Licence{
ID: claims.ID,
Subject: Subject{
PlatformName: claims.PlatformName,
Workspace: claims.Workspace,
},
IssuedOn: issuedOn,
ValidFrom: validFrom,
ExpiresOn: expiresOn,
Quotas: claims.Quotas,
}, nil
}