package service import ( "crypto/rand" "encoding/base64" "encoding/json" "errors" "fmt" "time" "git.zkcoi.com/zkcoi/meshray/internal/model" sqlite "git.zkcoi.com/zkcoi/meshray/internal/store/sqlite" "go.uber.org/zap" "golang.org/x/crypto/ed25519" "gorm.io/gorm" ) // MeshSeedService MeshSeed 服务 type MeshSeedService struct { store *sqlite.Store logger *zap.Logger signingKey ed25519.PrivateKey // Ed25519 签名密钥 issuerNodeID string // 签发节点 ID } // NewMeshSeedService 创建 MeshSeed 服务 func NewMeshSeedService(store *sqlite.Store, logger *zap.Logger, signingKey ed25519.PrivateKey, issuerNodeID string) *MeshSeedService { return &MeshSeedService{ store: store, logger: logger, signingKey: signingKey, issuerNodeID: issuerNodeID, } } // GenerateMeshSeed 生成 MeshSeed func (s *MeshSeedService) GenerateMeshSeed(networkID uint64, maxUses int, expiresAt time.Time, ddnsEnabled bool) (*model.MeshSeed, error) { // 1. 验证网络是否存在 var network model.Network if err := s.store.DB().First(&network, networkID).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, fmt.Errorf("网络不存在") } return nil, fmt.Errorf("查询网络失败:%w", err) } // 2. 生成随机 SeedID(16 字节随机数) seedBytes := make([]byte, 16) if _, err := rand.Read(seedBytes); err != nil { return nil, fmt.Errorf("生成随机数失败:%w", err) } seedID := base64.RawURLEncoding.EncodeToString(seedBytes) // 3. 构建 JoinToken(包含网络信息) joinTokenData := map[string]interface{}{ "seed_id": seedID, "network_id": networkID, "network_name": network.Name, "subnet_ipv4": network.SubnetIPv4, "mode": network.Mode, "ddns_enabled": ddnsEnabled, "expires_at": expiresAt.Unix(), "max_uses": maxUses, } // 序列化为 JSON tokenJSON, err := json.Marshal(joinTokenData) if err != nil { return nil, fmt.Errorf("序列化 Token 失败:%w", err) } // Base64 编码 joinToken := base64.StdEncoding.EncodeToString(tokenJSON) // 4. Ed25519 签名 signature := ed25519.Sign(s.signingKey, []byte(joinToken)) signatureStr := base64.StdEncoding.EncodeToString(signature) // 5. 创建 MeshSeed 记录 meshSeed := &model.MeshSeed{ SeedID: seedID, NetworkID: networkID, JoinToken: joinToken, Signature: signatureStr, IssuerNodeID: s.issuerNodeID, MaxUses: maxUses, UsedCount: 0, ExpiresAt: expiresAt, DDNSEnabled: ddnsEnabled, UpdateVersion: 0, Revoked: false, } if err := s.store.DB().Create(meshSeed).Error; err != nil { return nil, fmt.Errorf("创建 MeshSeed 失败:%w", err) } s.logger.Info("MeshSeed 已生成", zap.String("seed_id", seedID), zap.Uint64("network_id", networkID), zap.Int("max_uses", maxUses), zap.Time("expires_at", expiresAt)) return meshSeed, nil } // VerifyMeshSeed 验证 MeshSeed func (s *MeshSeedService) VerifyMeshSeed(joinToken, signature string) (*model.MeshSeed, error) { // 1. 解码 JoinToken tokenBytes, err := base64.StdEncoding.DecodeString(joinToken) if err != nil { return nil, fmt.Errorf("解码 Token 失败:%w", err) } // 2. 解码签名 sigBytes, err := base64.StdEncoding.DecodeString(signature) if err != nil { return nil, fmt.Errorf("解码签名失败:%w", err) } // 3. 验证 Ed25519 签名 publicKey := s.signingKey.Public() if !ed25519.Verify(publicKey.(ed25519.PublicKey), tokenBytes, sigBytes) { return nil, fmt.Errorf("签名验证失败") } // 4. 解析 Token 内容 var tokenData map[string]interface{} if err := json.Unmarshal(tokenBytes, &tokenData); err != nil { return nil, fmt.Errorf("解析 Token 失败:%w", err) } seedID, ok := tokenData["seed_id"].(string) if !ok { return nil, fmt.Errorf("Token 格式错误") } // 5. 查询 MeshSeed var meshSeed model.MeshSeed if err := s.store.DB().Where("seed_id = ?", seedID).First(&meshSeed).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, fmt.Errorf("MeshSeed 不存在") } return nil, fmt.Errorf("查询 MeshSeed 失败:%w", err) } // 6. 检查是否被吊销 if meshSeed.Revoked { return nil, fmt.Errorf("MeshSeed 已被吊销") } // 7. 检查使用次数 if meshSeed.UsedCount >= meshSeed.MaxUses { return nil, fmt.Errorf("MeshSeed 使用次数已用尽") } // 8. 检查过期时间 if time.Now().After(meshSeed.ExpiresAt) { return nil, fmt.Errorf("MeshSeed 已过期") } return &meshSeed, nil } // IncrementUseCount 增加使用次数 func (s *MeshSeedService) IncrementUseCount(seedID string) error { return s.store.DB().Transaction(func(tx *gorm.DB) error { var meshSeed model.MeshSeed if err := tx.Where("seed_id = ?", seedID).First(&meshSeed).Error; err != nil { return err } return tx.Model(&meshSeed).UpdateColumn("used_count", meshSeed.UsedCount+1).Error }) } // RevokeMeshSeed 吊销 MeshSeed func (s *MeshSeedService) RevokeMeshSeed(seedID string) error { result := s.store.DB().Model(&model.MeshSeed{}). Where("seed_id = ?", seedID). Update("revoked", true) if result.Error != nil { return fmt.Errorf("吊销 MeshSeed 失败:%w", result.Error) } if result.RowsAffected == 0 { return fmt.Errorf("MeshSeed 不存在") } s.logger.Info("MeshSeed 已吊销", zap.String("seed_id", seedID)) return nil } // ListMeshSeeds 获取网络的 MeshSeed 列表 func (s *MeshSeedService) ListMeshSeeds(networkID uint) ([]model.MeshSeed, error) { var seeds []model.MeshSeed err := s.store.DB().Where("network_id = ? AND revoked = ?", networkID, false). Order("created_at DESC"). Find(&seeds).Error return seeds, err }