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 }