112 lines
3.4 KiB
Go

package storage
import (
"bytes"
"crypto/rand"
"encoding/hex"
"fmt"
"log"
"regexp"
"strings"
"time"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/armor"
)
func extractEmail(fullNameWithEmail string) string {
re := regexp.MustCompile(`<([^>]+)>`)
matches := re.FindStringSubmatch(fullNameWithEmail)
if len(matches) > 1 {
return matches[1]
}
return ""
}
// ConverterPgpToService converts a raw openpgp.EntityList into a slice of service.PGPkey objects.
// It parses key data, generates verification tokens for UIDs, and extracts email addresses.
//
// Parameters:
//
// entities: The openpgp.EntityList obtained from parsing an armored PGP block.
//
// Returns:
//
// []*PGPkey: A slice of converted PGPkey objects. Returns an empty (non-nil) slice if no valid entities are found.
// error: An error if any entity fails to be processed or contains invalid data.
func ConverterPgpToService(entities openpgp.EntityList) ([]*PGPkey, error) {
var pgpKeys []*PGPkey
if entities == nil {
return pgpKeys, nil
}
for i, entity := range entities {
// Test on correct data
if entity == nil {
log.Printf("Warning: ConverterPgpToService: encountered nil entity at index %d, skipping.", i)
continue
}
if entity.PrimaryKey == nil {
log.Printf("Warning: ConverterPgpToService: entity at index %d has no primary key, skipping.", i)
continue
}
publicKeyBuf := bytes.NewBuffer(nil)
publicKeyWriter, err := armor.Encode(publicKeyBuf, openpgp.PublicKeyType, nil)
if err != nil {
return nil, fmt.Errorf("converter error: entity %d: failed to create armor encoder: %w", i, err)
}
if err = entity.Serialize(publicKeyWriter); err != nil {
return nil, fmt.Errorf("converter error: entity %d: failed to serialize public key: %w", i, err)
}
publicKeyWriter.Close()
// Write it
pgpKey := PGPkey{
Revoked: entity.Revoked(time.Now()),
UpdateTime: entity.PrimaryKey.CreationTime.UTC(), // <--- FIX IS HERE! Use PrimaryKey's CreationTime
Fingerprint: strings.ToUpper(hex.EncodeToString(entity.PrimaryKey.Fingerprint)),
Packet: publicKeyBuf.String(),
Uids: []*PGPUid{},
}
// Process each UID for the current entity
for _, uid := range entity.Identities {
if uid == nil || uid.UserId == nil {
log.Printf("Warning: ConverterPgpToService: encountered nil Identity or UserId for key %s, skipping.", pgpKey.Fingerprint)
continue
}
if uid.Name == "" {
log.Printf("Warning: ConverterPgpToService: encountered empty User ID string for key %s, skipping.", pgpKey.Fingerprint)
continue
}
randBytes := make([]byte, 16)
if _, err := rand.Read(randBytes); err != nil {
return nil, fmt.Errorf("converter error: key %s: failed to generate random bytes for token: %w", pgpKey.Fingerprint, err)
}
token := hex.EncodeToString(randBytes)
extractedEmail := extractEmail(uid.Name)
if extractedEmail == "" {
log.Printf("Warning: ConverterPgpToService: extracted empty email from UID '%s' for key %s, skipping UID.", uid.Name, pgpKey.Fingerprint)
continue
}
pgpKey.Uids = append(pgpKey.Uids, &PGPUid{
Verify: false,
UIDString: uid.Name,
Email: extractedEmail,
Token: token,
TokenExpires: time.Now().Add(time.Hour * 5),
Fingerprint: pgpKey.Fingerprint,
})
}
pgpKeys = append(pgpKeys, &pgpKey)
}
return pgpKeys, nil
}