158 lines
3.8 KiB
Go
158 lines
3.8 KiB
Go
package cmd
|
|
|
|
import (
|
|
"archive/zip"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"omixlab.com/mosis-portal/pkg/mospkg"
|
|
)
|
|
|
|
// SignCmd returns the sign command
|
|
func SignCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "sign <package>",
|
|
Short: "Sign a .mosis package",
|
|
Long: "Sign a Mosis package with your Ed25519 developer key.",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: runSign,
|
|
}
|
|
|
|
cmd.Flags().StringP("key", "k", "", "Path to private key (default: ~/.mosis/signing_key.pem)")
|
|
cmd.Flags().Bool("verify", false, "Verify existing signature instead of signing")
|
|
cmd.Flags().StringP("output", "o", "", "Output path (default: overwrite input)")
|
|
|
|
return cmd
|
|
}
|
|
|
|
func runSign(cmd *cobra.Command, args []string) error {
|
|
packagePath := args[0]
|
|
|
|
// Check package exists
|
|
if _, err := os.Stat(packagePath); os.IsNotExist(err) {
|
|
return fmt.Errorf("package not found: %s", packagePath)
|
|
}
|
|
|
|
// Verify mode
|
|
verify, _ := cmd.Flags().GetBool("verify")
|
|
if verify {
|
|
return verifyPackage(cmd, packagePath)
|
|
}
|
|
|
|
// Sign mode
|
|
keyPath, _ := cmd.Flags().GetString("key")
|
|
if keyPath == "" {
|
|
keyPath = filepath.Join(ConfigDir(), "signing_key.pem")
|
|
}
|
|
|
|
// Load private key
|
|
keyData, err := os.ReadFile(keyPath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read key: %s\n\nGenerate a key with: mosis keys generate", keyPath)
|
|
}
|
|
|
|
privateKey, err := mospkg.LoadPrivateKey(keyData)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid key: %w", err)
|
|
}
|
|
|
|
// Get public key fingerprint
|
|
publicKey := privateKey.Public().(interface{ Bytes() []byte })
|
|
fingerprint := mospkg.PublicKeyFingerprint(publicKey.Bytes())
|
|
|
|
fmt.Printf("Using key: %s\n", keyPath)
|
|
fmt.Printf("Fingerprint: %s\n\n", fingerprint)
|
|
|
|
// Determine output path
|
|
output, _ := cmd.Flags().GetString("output")
|
|
if output == "" {
|
|
// Sign in place by creating temp file
|
|
output = packagePath + ".signed"
|
|
}
|
|
|
|
// Generate manifest and sign
|
|
fmt.Println("Generating file hashes...")
|
|
fmt.Println("Signing MANIFEST.MF...")
|
|
|
|
if err := mospkg.SignPackage(packagePath, output, privateKey); err != nil {
|
|
return fmt.Errorf("sign package: %w", err)
|
|
}
|
|
|
|
// If signing in place, replace original
|
|
if output == packagePath+".signed" {
|
|
if err := os.Rename(output, packagePath); err != nil {
|
|
return fmt.Errorf("replace original: %w", err)
|
|
}
|
|
output = packagePath
|
|
}
|
|
|
|
// Count files in package
|
|
fileCount := countPackageFiles(output)
|
|
|
|
fmt.Printf("\n✓ Package signed: %s\n", output)
|
|
fmt.Println("\nSignature details:")
|
|
fmt.Println(" Algorithm: Ed25519")
|
|
fmt.Printf(" Key fingerprint: %s\n", fingerprint)
|
|
fmt.Printf(" Files signed: %d\n", fileCount)
|
|
|
|
return nil
|
|
}
|
|
|
|
func verifyPackage(cmd *cobra.Command, packagePath string) error {
|
|
keyPath, _ := cmd.Flags().GetString("key")
|
|
|
|
// Try to load public key
|
|
var pubKeyPath string
|
|
if keyPath != "" {
|
|
pubKeyPath = keyPath
|
|
} else {
|
|
// Try default locations
|
|
pubKeyPath = filepath.Join(ConfigDir(), "signing_key.pub")
|
|
}
|
|
|
|
keyData, err := os.ReadFile(pubKeyPath)
|
|
if err != nil {
|
|
return fmt.Errorf("public key not found: %s\n\nProvide key with --key flag", pubKeyPath)
|
|
}
|
|
|
|
publicKey, err := mospkg.LoadPublicKey(keyData)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid public key: %w", err)
|
|
}
|
|
|
|
fmt.Printf("Verifying: %s\n", packagePath)
|
|
fmt.Printf("Using key: %s\n\n", pubKeyPath)
|
|
|
|
valid, err := mospkg.VerifyPackageSignature(packagePath, publicKey)
|
|
if err != nil {
|
|
return fmt.Errorf("verification failed: %w", err)
|
|
}
|
|
|
|
if valid {
|
|
fmt.Println("✓ Signature is valid")
|
|
fmt.Println("✓ All file hashes match")
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("✗ Signature verification failed")
|
|
}
|
|
|
|
func countPackageFiles(path string) int {
|
|
reader, err := zip.OpenReader(path)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
defer reader.Close()
|
|
|
|
count := 0
|
|
for _, f := range reader.File {
|
|
if !f.FileInfo().IsDir() {
|
|
count++
|
|
}
|
|
}
|
|
return count
|
|
}
|