package service import ( "bufio" "strings" "github.com/rs/zerolog" "github.com/gosec/gsc-ops-api/internal/client" "github.com/gosec/gsc-ops-api/pkg/types" ) // PGPService handles Hockeypuck PGP key operations type PGPService struct { client *client.HockeypuckClient logger zerolog.Logger } // NewPGPService creates a new PGP service func NewPGPService(hkpClient *client.HockeypuckClient, logger zerolog.Logger) *PGPService { return &PGPService{ client: hkpClient, logger: logger.With().Str("service", "pgp").Logger(), } } // SearchKeys searches for PGP keys func (s *PGPService) SearchKeys(query string) ([]types.PGPKey, error) { result, err := s.client.SearchKeys(query) if err != nil { return nil, err } if result == "" { return []types.PGPKey{}, nil } return parseMachineReadableIndex(result), nil } // GetKey retrieves a PGP key by key ID func (s *PGPService) GetKey(keyID string) (*types.PGPKey, error) { armoredKey, err := s.client.GetKey(keyID) if err != nil { return nil, err } if armoredKey == "" { return nil, nil } return &types.PGPKey{ KeyID: keyID, ArmoredKey: armoredKey, }, nil } // UploadKey uploads a PGP public key func (s *PGPService) UploadKey(keyText string) error { return s.client.UploadKey(keyText) } // DeleteKey deletes a PGP key func (s *PGPService) DeleteKey(keyID string) error { return s.client.DeleteKey(keyID) } // parseMachineReadableIndex parses the HKP machine-readable index format func parseMachineReadableIndex(data string) []types.PGPKey { keys := []types.PGPKey{} var current *types.PGPKey scanner := bufio.NewScanner(strings.NewReader(data)) for scanner.Scan() { line := scanner.Text() fields := strings.Split(line, ":") if len(fields) < 2 { continue } switch fields[0] { case "pub": if current != nil { keys = append(keys, *current) } current = &types.PGPKey{} if len(fields) > 1 { current.KeyID = fields[1] } if len(fields) > 2 { current.Algorithm = fields[2] } if len(fields) > 4 { current.Created = fields[4] } if len(fields) > 5 { current.Expires = fields[5] } case "uid": if current != nil && len(fields) > 1 { current.UIDs = append(current.UIDs, fields[1]) } } } if current != nil { keys = append(keys, *current) } return keys }