did stuff
Some checks failed
Tests / 🛃 Unit tests (push) Failing after 13s
Tests / 🗄 DB tests (push) Failing after 19s
Tests / 🐍 Lint python scripts (push) Failing after 1s
Tests / ⌨ Golang Lint (push) Failing after 1s
Tests / 📦 Packit config lint (push) Failing after 1s
Tests / 🔍 Check source preparation (push) Failing after 1s
Tests / 🔍 Check for valid snapshot urls (push) Failing after 1s
Tests / 🔍 Check for missing or unused runner repos (push) Failing after 1s
Tests / 🐚 Shellcheck (push) Failing after 1s
Tests / 📦 RPMlint (push) Failing after 1s
Tests / Gitlab CI trigger helper (push) Failing after 1s
Tests / 🎀 kube-linter (push) Failing after 1s
Tests / 🧹 cloud-cleaner-is-enabled (push) Successful in 3s
Tests / 🔍 Check spec file osbuild/images dependencies (push) Failing after 1s

This commit is contained in:
robojerk 2025-08-26 10:34:42 -07:00
parent d228f6d30f
commit 4eeaa43c39
47 changed files with 21390 additions and 31 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,856 @@
package security
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/sirupsen/logrus"
)
type SigningVerifier struct {
logger *logrus.Logger
config *SigningConfig
gpg *GPGManager
cosign *CosignManager
keyManager *KeyManager
trustStore *TrustStore
}
type SigningConfig struct {
GPGHomeDir string `json:"gpg_home_dir"`
CosignKeyPath string `json:"cosign_key_path"`
TrustStorePath string `json:"trust_store_path"`
KeyRingPath string `json:"key_ring_path"`
SigningKeyID string `json:"signing_key_id"`
VerifySignatures bool `json:"verify_signatures"`
Metadata map[string]string `json:"metadata"`
}
type GPGManager struct {
homeDir string
keyRing string
signingKey string
logger *logrus.Logger
}
type CosignManager struct {
keyPath string
password string
logger *logrus.Logger
}
type KeyManager struct {
keys map[string]SigningKey
keyRing string
logger *logrus.Logger
}
type TrustStore struct {
trustedKeys map[string]TrustedKey
caCerts map[string]CACertificate
crls map[string]CRL
logger *logrus.Logger
}
type SigningKey struct {
ID string `json:"id"`
Type string `json:"type"`
Algorithm string `json:"algorithm"`
KeySize int `json:"key_size"`
Fingerprint string `json:"fingerprint"`
Created time.Time `json:"created"`
Expires time.Time `json:"expires"`
UserID string `json:"user_id"`
Email string `json:"email"`
PublicKey string `json:"public_key"`
PrivateKey string `json:"private_key,omitempty"`
Metadata map[string]interface{} `json:"metadata"`
}
type TrustedKey struct {
ID string `json:"id"`
Fingerprint string `json:"fingerprint"`
TrustLevel string `json:"trust_level"`
Added time.Time `json:"added"`
Expires time.Time `json:"expires"`
Metadata map[string]interface{} `json:"metadata"`
}
type CACertificate struct {
ID string `json:"id"`
Subject string `json:"subject"`
Issuer string `json:"issuer"`
ValidFrom time.Time `json:"valid_from"`
ValidTo time.Time `json:"valid_to"`
Fingerprint string `json:"fingerprint"`
PEM string `json:"pem"`
Metadata map[string]interface{} `json:"metadata"`
}
type CRL struct {
ID string `json:"id"`
Issuer string `json:"issuer"`
ThisUpdate time.Time `json:"this_update"`
NextUpdate time.Time `json:"next_update"`
Revoked []RevokedCertificate `json:"revoked"`
Metadata map[string]interface{} `json:"metadata"`
}
type RevokedCertificate struct {
SerialNumber string `json:"serial_number"`
RevocationDate time.Time `json:"revocation_date"`
Reason string `json:"reason"`
}
type SignatureResult struct {
ID string `json:"id"`
Target string `json:"target"`
TargetType string `json:"target_type"`
Signer string `json:"signer"`
Algorithm string `json:"algorithm"`
Signature string `json:"signature"`
Timestamp time.Time `json:"timestamp"`
Valid bool `json:"valid"`
Verified bool `json:"verified"`
Metadata map[string]interface{} `json:"metadata"`
}
type VerificationResult struct {
ID string `json:"id"`
Target string `json:"target"`
TargetType string `json:"target_type"`
Signatures []SignatureResult `json:"signatures"`
Valid bool `json:"valid"`
TrustChain []TrustLink `json:"trust_chain"`
Warnings []string `json:"warnings"`
Errors []string `json:"errors"`
Metadata map[string]interface{} `json:"metadata"`
}
type TrustLink struct {
From string `json:"from"`
To string `json:"to"`
Type string `json:"type"`
Algorithm string `json:"algorithm"`
Valid bool `json:"valid"`
Metadata map[string]interface{} `json:"metadata"`
}
func NewSigningVerifier(config *SigningConfig, logger *logrus.Logger) *SigningVerifier {
verifier := &SigningVerifier{
logger: logger,
config: config,
gpg: NewGPGManager(config.GPGHomeDir, config.KeyRingPath, config.SigningKeyID, logger),
cosign: NewCosignManager(config.CosignKeyPath, logger),
keyManager: NewKeyManager(config.KeyRingPath, logger),
trustStore: NewTrustStore(config.TrustStorePath, logger),
}
return verifier
}
func NewGPGManager(homeDir, keyRing, signingKey string, logger *logrus.Logger) *GPGManager {
return &GPGManager{
homeDir: homeDir,
keyRing: keyRing,
signingKey: signingKey,
logger: logger,
}
}
func NewCosignManager(keyPath string, logger *logrus.Logger) *CosignManager {
return &CosignManager{
keyPath: keyPath,
logger: logger,
}
}
func NewKeyManager(keyRing string, logger *logrus.Logger) *KeyManager {
return &KeyManager{
keys: make(map[string]SigningKey),
keyRing: keyRing,
logger: logger,
}
}
func NewTrustStore(trustStorePath string, logger *logrus.Logger) *TrustStore {
return &TrustStore{
trustedKeys: make(map[string]TrustedKey),
caCerts: make(map[string]CACertificate),
crls: make(map[string]CRL),
logger: logger,
}
}
func (sv *SigningVerifier) SignTarget(target string, targetType string, algorithm string) (*SignatureResult, error) {
sv.logger.Infof("Signing target: %s (type: %s, algorithm: %s)", target, targetType, algorithm)
// Create signature result
result := &SignatureResult{
ID: generateSignatureID(),
Target: target,
TargetType: targetType,
Algorithm: algorithm,
Timestamp: time.Now(),
Metadata: make(map[string]interface{}),
}
// Sign based on target type
switch targetType {
case "package", "deb":
if err := sv.signPackage(target, result); err != nil {
return nil, fmt.Errorf("package signing failed: %w", err)
}
case "container", "image":
if err := sv.signContainer(target, result); err != nil {
return nil, fmt.Errorf("container signing failed: %w", err)
}
case "file":
if err := sv.signFile(target, result); err != nil {
return nil, fmt.Errorf("file signing failed: %w", err)
}
default:
return nil, fmt.Errorf("unsupported target type: %s", targetType)
}
sv.logger.Infof("Successfully signed target: %s", target)
return result, nil
}
func (sv *SigningVerifier) signPackage(target string, result *SignatureResult) error {
// Use dpkg-sig for Debian package signing
cmd := exec.Command("dpkg-sig", "--sign", "origin", target)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("dpkg-sig failed: %w", err)
}
// Get signature information
result.Signer = sv.config.SigningKeyID
result.Valid = true
// Extract signature from package
if err := sv.extractPackageSignature(target, result); err != nil {
sv.logger.Warnf("Failed to extract package signature: %v", err)
}
return nil
}
func (sv *SigningVerifier) signContainer(target string, result *SignatureResult) error {
// Use cosign for container signing
cmd := exec.Command("cosign", "sign", "--key", sv.config.CosignKeyPath, target)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("cosign signing failed: %w", err)
}
// Get signature information
result.Signer = "cosign"
result.Valid = true
// Extract signature from container
if err := sv.extractContainerSignature(target, result); err != nil {
sv.logger.Warnf("Failed to extract container signature: %v", err)
}
return nil
}
func (sv *SigningVerifier) signFile(target string, result *SignatureResult) error {
// Use GPG for file signing
cmd := exec.Command("gpg", "--detach-sign", "--armor", "--local-user", sv.config.SigningKeyID, target)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("GPG signing failed: %w", err)
}
// Get signature information
result.Signer = sv.config.SigningKeyID
result.Valid = true
// Extract signature from file
if err := sv.extractFileSignature(target, result); err != nil {
sv.logger.Warnf("Failed to extract file signature: %v", err)
}
return nil
}
func (sv *SigningVerifier) extractPackageSignature(target string, result *SignatureResult) error {
// Extract signature from Debian package
cmd := exec.Command("dpkg-sig", "--verify", target)
output, err := cmd.Output()
if err != nil {
return fmt.Errorf("dpkg-sig verify failed: %w", err)
}
// Parse output to extract signature
lines := strings.Split(string(output), "\n")
for _, line := range lines {
if strings.Contains(line, "GOODSIG") {
parts := strings.Fields(line)
if len(parts) >= 3 {
result.Signature = parts[2]
break
}
}
}
return nil
}
func (sv *SigningVerifier) extractContainerSignature(target string, result *SignatureResult) error {
// Extract cosign signature
cmd := exec.Command("cosign", "verify", "--key", sv.config.CosignKeyPath, target)
output, err := cmd.Output()
if err != nil {
return fmt.Errorf("cosign verify failed: %w", err)
}
// Parse output to extract signature
lines := strings.Split(string(output), "\n")
for _, line := range lines {
if strings.Contains(line, "Signature:") {
parts := strings.Split(line, ":")
if len(parts) >= 2 {
result.Signature = strings.TrimSpace(parts[1])
break
}
}
}
return nil
}
func (sv *SigningVerifier) extractFileSignature(target string, result *SignatureResult) error {
// Read GPG signature file
sigFile := target + ".asc"
if _, err := os.Stat(sigFile); os.IsNotExist(err) {
return fmt.Errorf("signature file not found: %s", sigFile)
}
sigData, err := os.ReadFile(sigFile)
if err != nil {
return fmt.Errorf("failed to read signature file: %w", err)
}
result.Signature = base64.StdEncoding.EncodeToString(sigData)
return nil
}
func (sv *SigningVerifier) VerifyTarget(target string, targetType string) (*VerificationResult, error) {
sv.logger.Infof("Verifying target: %s (type: %s)", target, targetType, targetType)
// Create verification result
result := &VerificationResult{
ID: generateVerificationID(),
Target: target,
TargetType: targetType,
Signatures: []SignatureResult{},
TrustChain: []TrustLink{},
Warnings: []string{},
Errors: []string{},
Metadata: make(map[string]interface{}),
}
// Verify based on target type
switch targetType {
case "package", "deb":
if err := sv.verifyPackage(target, result); err != nil {
result.Errors = append(result.Errors, err.Error())
}
case "container", "image":
if err := sv.verifyContainer(target, result); err != nil {
result.Errors = append(result.Errors, err.Error())
}
case "file":
if err := sv.verifyFile(target, result); err != nil {
result.Errors = append(result.Errors, err.Error())
}
default:
return nil, fmt.Errorf("unsupported target type: %s", targetType)
}
// Verify trust chain
if err := sv.verifyTrustChain(result); err != nil {
result.Warnings = append(result.Warnings, "Trust chain verification failed: "+err.Error())
}
// Determine overall validity
result.Valid = len(result.Errors) == 0 && len(result.Signatures) > 0
sv.logger.Infof("Verification completed for target: %s, valid: %t", target, result.Valid)
return result, nil
}
func (sv *SigningVerifier) verifyPackage(target string, result *VerificationResult) error {
// Use dpkg-sig for Debian package verification
cmd := exec.Command("dpkg-sig", "--verify", target)
output, err := cmd.Output()
if err != nil {
return fmt.Errorf("dpkg-sig verify failed: %w", err)
}
// Parse verification output
lines := strings.Split(string(output), "\n")
for _, line := range lines {
if strings.Contains(line, "GOODSIG") {
parts := strings.Fields(line)
if len(parts) >= 3 {
signature := SignatureResult{
ID: generateSignatureID(),
Target: target,
TargetType: "package",
Signer: parts[2],
Algorithm: "RSA",
Valid: true,
Verified: true,
Timestamp: time.Now(),
Metadata: make(map[string]interface{}),
}
result.Signatures = append(result.Signatures, signature)
}
} else if strings.Contains(line, "BADSIG") {
result.Errors = append(result.Errors, "Bad signature detected")
}
}
return nil
}
func (sv *SigningVerifier) verifyContainer(target string, result *VerificationResult) error {
// Use cosign for container verification
cmd := exec.Command("cosign", "verify", "--key", sv.config.CosignKeyPath, target)
output, err := cmd.Output()
if err != nil {
return fmt.Errorf("cosign verify failed: %w", err)
}
// Parse verification output
lines := strings.Split(string(output), "\n")
for _, line := range lines {
if strings.Contains(line, "Verified") {
signature := SignatureResult{
ID: generateSignatureID(),
Target: target,
TargetType: "container",
Signer: "cosign",
Algorithm: "ECDSA",
Valid: true,
Verified: true,
Timestamp: time.Now(),
Metadata: make(map[string]interface{}),
}
result.Signatures = append(result.Signatures, signature)
}
}
return nil
}
func (sv *SigningVerifier) verifyFile(target string, result *VerificationResult) error {
// Use GPG for file verification
cmd := exec.Command("gpg", "--verify", target+".asc", target)
output, err := cmd.Output()
if err != nil {
return fmt.Errorf("GPG verify failed: %w", err)
}
// Parse verification output
lines := strings.Split(string(output), "\n")
for _, line := range lines {
if strings.Contains(line, "Good signature") {
parts := strings.Fields(line)
if len(parts) >= 3 {
signature := SignatureResult{
ID: generateSignatureID(),
Target: target,
TargetType: "file",
Signer: parts[2],
Algorithm: "RSA",
Valid: true,
Verified: true,
Timestamp: time.Now(),
Metadata: make(map[string]interface{}),
}
result.Signatures = append(result.Signatures, signature)
}
} else if strings.Contains(line, "Bad signature") {
result.Errors = append(result.Errors, "Bad signature detected")
}
}
return nil
}
func (sv *SigningVerifier) verifyTrustChain(result *VerificationResult) error {
for _, signature := range result.Signatures {
// Verify key trust
if err := sv.verifyKeyTrust(signature.Signer, result); err != nil {
sv.logger.Warnf("Key trust verification failed for %s: %v", signature.Signer, err)
}
// Verify certificate chain if applicable
if err := sv.verifyCertificateChain(signature.Signer, result); err != nil {
sv.logger.Warnf("Certificate chain verification failed for %s: %v", signature.Signer, err)
}
}
return nil
}
func (sv *SigningVerifier) verifyKeyTrust(keyID string, result *VerificationResult) error {
// Check if key is in trusted key store
if trustedKey, exists := sv.trustStore.trustedKeys[keyID]; exists {
trustLink := TrustLink{
From: keyID,
To: "trusted_key_store",
Type: "trusted_key",
Algorithm: "GPG",
Valid: true,
Metadata: make(map[string]interface{}),
}
result.TrustChain = append(result.TrustChain, trustLink)
return nil
}
// Check GPG key trust
cmd := exec.Command("gpg", "--list-keys", "--with-colons", keyID)
output, err := cmd.Output()
if err != nil {
return fmt.Errorf("failed to list GPG key: %w", err)
}
// Parse trust level
lines := strings.Split(string(output), "\n")
for _, line := range lines {
if strings.HasPrefix(line, "pub:") {
parts := strings.Split(line, ":")
if len(parts) >= 10 {
trustLevel := parts[1]
if trustLevel == "f" || trustLevel == "u" {
trustLink := TrustLink{
From: keyID,
To: "gpg_trusted",
Type: "gpg_trust",
Algorithm: "GPG",
Valid: true,
Metadata: make(map[string]interface{}),
}
result.TrustChain = append(result.TrustChain, trustLink)
return nil
}
}
}
}
result.Warnings = append(result.Warnings, fmt.Sprintf("Key %s not in trusted store", keyID))
return nil
}
func (sv *SigningVerifier) verifyCertificateChain(keyID string, result *VerificationResult) error {
// Check for X.509 certificates
cmd := exec.Command("gpg", "--export", keyID)
output, err := cmd.Output()
if err != nil {
return fmt.Errorf("failed to export GPG key: %w", err)
}
// Try to parse as X.509 certificate
if len(output) > 0 {
block, _ := pem.Decode(output)
if block != nil && block.Type == "CERTIFICATE" {
cert, err := x509.ParseCertificate(block.Bytes)
if err == nil {
// Verify certificate chain
if err := sv.verifyX509Chain(cert, result); err != nil {
sv.logger.Warnf("X.509 chain verification failed: %v", err)
}
}
}
}
return nil
}
func (sv *SigningVerifier) verifyX509Chain(cert *x509.Certificate, result *VerificationResult) error {
// Check if certificate is in CA store
for _, caCert := range sv.trustStore.caCerts {
if caCert.Fingerprint == sv.calculateFingerprint(cert.Raw) {
trustLink := TrustLink{
From: cert.Subject.CommonName,
To: caCert.Subject.CommonName,
Type: "x509_ca",
Algorithm: "RSA",
Valid: true,
Metadata: make(map[string]interface{}),
}
result.TrustChain = append(result.TrustChain, trustLink)
return nil
}
}
// Check system CA store
roots := x509.NewCertPool()
if ok := roots.AppendCertsFromPEM([]byte(sv.getSystemCAs())); ok {
opts := x509.VerifyOptions{
Roots: roots,
}
if _, err := cert.Verify(opts); err == nil {
trustLink := TrustLink{
From: cert.Subject.CommonName,
To: "system_ca_store",
Type: "x509_system",
Algorithm: "RSA",
Valid: true,
Metadata: make(map[string]interface{}),
}
result.TrustChain = append(result.TrustChain, trustLink)
return nil
}
}
result.Warnings = append(result.Warnings, "Certificate not in trusted CA store")
return nil
}
func (sv *SigningVerifier) calculateFingerprint(data []byte) string {
hash := sha256.Sum256(data)
return fmt.Sprintf("%x", hash)
}
func (sv *SigningVerifier) getSystemCAs() string {
// Common CA certificate locations
caPaths := []string{
"/etc/ssl/certs/ca-certificates.crt",
"/etc/ssl/certs/ca-bundle.crt",
"/usr/share/ssl/certs/ca-bundle.crt",
}
for _, path := range caPaths {
if data, err := os.ReadFile(path); err == nil {
return string(data)
}
}
return ""
}
func (sv *SigningVerifier) GenerateKeyPair(keyType string, keySize int, userID string, email string) (*SigningKey, error) {
sv.logger.Infof("Generating %s key pair (size: %d) for %s <%s>", keyType, keySize, userID, email)
key := &SigningKey{
ID: generateKeyID(),
Type: keyType,
Algorithm: "RSA",
KeySize: keySize,
Created: time.Now(),
Expires: time.Now().AddDate(2, 0, 0), // 2 years
UserID: userID,
Email: email,
Metadata: make(map[string]interface{}),
}
switch keyType {
case "gpg":
if err := sv.generateGPGKey(key); err != nil {
return nil, fmt.Errorf("GPG key generation failed: %w", err)
}
case "cosign":
if err := sv.generateCosignKey(key); err != nil {
return nil, fmt.Errorf("Cosign key generation failed: %w", err)
}
default:
return nil, fmt.Errorf("unsupported key type: %s", keyType)
}
// Add to key manager
sv.keyManager.keys[key.ID] = *key
sv.logger.Infof("Successfully generated key: %s", key.ID)
return key, nil
}
func (sv *SigningVerifier) generateGPGKey(key *SigningKey) error {
// Generate GPG key using gpg command
cmd := exec.Command("gpg", "--batch", "--gen-key", "--yes")
// Create batch file for key generation
batchContent := fmt.Sprintf(`Key-Type: RSA
Key-Length: %d
Name-Real: %s
Name-Email: %s
Expire-Date: 2y
%commit
`, key.KeySize, key.UserID, key.Email)
batchFile := filepath.Join(sv.config.GPGHomeDir, "batch.txt")
if err := os.WriteFile(batchFile, []byte(batchContent), 0600); err != nil {
return fmt.Errorf("failed to write batch file: %w", err)
}
defer os.Remove(batchFile)
cmd = exec.Command("gpg", "--batch", "--gen-key", batchFile)
cmd.Dir = sv.config.GPGHomeDir
if err := cmd.Run(); err != nil {
return fmt.Errorf("GPG key generation failed: %w", err)
}
// Export public key
exportCmd := exec.Command("gpg", "--armor", "--export", key.Email)
exportCmd.Dir = sv.config.GPGHomeDir
publicKey, err := exportCmd.Output()
if err != nil {
return fmt.Errorf("failed to export public key: %w", err)
}
key.PublicKey = string(publicKey)
// Get fingerprint
fingerprintCmd := exec.Command("gpg", "--fingerprint", key.Email)
fingerprintCmd.Dir = sv.config.GPGHomeDir
fingerprintOutput, err := fingerprintCmd.Output()
if err != nil {
return fmt.Errorf("failed to get fingerprint: %w", err)
}
// Parse fingerprint from output
lines := strings.Split(string(fingerprintOutput), "\n")
for _, line := range lines {
if strings.Contains(line, "Key fingerprint =") {
parts := strings.Split(line, "=")
if len(parts) >= 2 {
key.Fingerprint = strings.TrimSpace(parts[1])
break
}
}
}
return nil
}
func (sv *SigningVerifier) generateCosignKey(key *SigningKey) error {
// Generate cosign key pair
cmd := exec.Command("cosign", "generate-key-pair")
if err := cmd.Run(); err != nil {
return fmt.Errorf("cosign key generation failed: %w", err)
}
// Read generated keys
cosignKey := sv.config.CosignKeyPath
if cosignKey == "" {
cosignKey = "cosign.key"
}
privateKey, err := os.ReadFile(cosignKey)
if err != nil {
return fmt.Errorf("failed to read private key: %w", err)
}
publicKey, err := os.ReadFile(cosignKey + ".pub")
if err != nil {
return fmt.Errorf("failed to read public key: %w", err)
}
key.PrivateKey = string(privateKey)
key.PublicKey = string(publicKey)
key.Algorithm = "ECDSA"
// Generate fingerprint
hash := sha256.Sum256(publicKey)
key.Fingerprint = fmt.Sprintf("%x", hash)
return nil
}
func (sv *SigningVerifier) AddTrustedKey(key *SignedKey) error {
sv.logger.Infof("Adding trusted key: %s", key.ID)
trustedKey := TrustedKey{
ID: key.ID,
Fingerprint: key.Fingerprint,
TrustLevel: "trusted",
Added: time.Now(),
Expires: key.Expires,
Metadata: make(map[string]interface{}),
}
sv.trustStore.trustedKeys[key.ID] = trustedKey
// Import into GPG keyring
if err := sv.importGPGKey(key.PublicKey); err != nil {
sv.logger.Warnf("Failed to import GPG key: %v", err)
}
sv.logger.Infof("Successfully added trusted key: %s", key.ID)
return nil
}
func (sv *SigningVerifier) importGPGKey(publicKey string) error {
// Create temporary file for public key
tempFile, err := os.CreateTemp("", "gpg-key-*")
if err != nil {
return fmt.Errorf("failed to create temp file: %w", err)
}
defer os.Remove(tempFile.Name())
if _, err := tempFile.WriteString(publicKey); err != nil {
return fmt.Errorf("failed to write public key: %w", err)
}
tempFile.Close()
// Import key into GPG
cmd := exec.Command("gpg", "--import", tempFile.Name())
cmd.Dir = sv.config.GPGHomeDir
if err := cmd.Run(); err != nil {
return fmt.Errorf("GPG import failed: %w", err)
}
return nil
}
// Helper functions
func generateSignatureID() string {
return fmt.Sprintf("sig-%d", time.Now().UnixNano())
}
func generateVerificationID() string {
return fmt.Sprintf("ver-%d", time.Now().UnixNano())
}
func generateKeyID() string {
return fmt.Sprintf("key-%d", time.Now().UnixNano())
}
// SignedKey type for trusted key addition
type SignedKey struct {
ID string `json:"id"`
Fingerprint string `json:"fingerprint"`
Expires time.Time `json:"expires"`
Metadata map[string]interface{} `json:"metadata"`
}