package backend import ( "archive/zip" "bytes" "context" "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/pem" "errors" "fmt" "io" "os" "path/filepath" "github.com/wailsapp/wails/v2/pkg/runtime" ) type Decryptor struct { ctx context.Context } func NewDecryptor() *Decryptor { return &Decryptor{} } func (d *Decryptor) OnStartup(ctx context.Context) { d.ctx = ctx fmt.Printf("ctx: %v\n", ctx) } func (d *Decryptor) SelectPrivateKeyFile() (string, error) { return runtime.OpenFileDialog(d.ctx, runtime.OpenDialogOptions{ Title: "Select Private Key File", Filters: []runtime.FileFilter{ {DisplayName: "PEM Files", Pattern: "*.pem"}, }, }) } func (d *Decryptor) SelectEncryptedFile() (string, error) { return runtime.OpenFileDialog(d.ctx, runtime.OpenDialogOptions{ Title: "Select Encrypted Zip File", Filters: []runtime.FileFilter{ {DisplayName: "Encrypted Zip", Pattern: "*.zip"}, }, }) } func (d *Decryptor) SelectOutputDirectory() (string, error) { return runtime.OpenDirectoryDialog(d.ctx, runtime.OpenDialogOptions{ Title: "Select Output Directory", }) } func (d *Decryptor) DecryptHybridZip(encryptedZipPath string, privKeyPath string, outputDir string) error { r, err := zip.OpenReader(encryptedZipPath) if err != nil { return err } defer r.Close() var encData, encKey []byte for _, f := range r.File { rc, _ := f.Open() data, _ := io.ReadAll(rc) rc.Close() fmt.Printf("f.Name: %v\n", f.Name) switch f.Name { case "encrypted_data.bin": encData = data case "encrypted_key.bin": encKey = data } } if encData == nil || encKey == nil { return errors.New("missing encrypted files") } privKey, err := loadPrivateKey(privKeyPath) if err != nil { return err } aesKey, err := rsa.DecryptPKCS1v15(rand.Reader, privKey, encKey) if err != nil { return err } zipData, err := decryptAES(encData, aesKey) if err != nil { return err } zipReader, err := zip.NewReader(bytes.NewReader(zipData), int64(len(zipData))) if err != nil { return err } var originalFolderName string for _, f := range zipReader.File { dirPath := filepath.Dir(f.Name) if len(dirPath) > 0 && originalFolderName == "" { originalFolderName = dirPath } } if originalFolderName == "" { return errors.New("failed to detect original folder name") } outputZip := filepath.Join(outputDir, originalFolderName+".zip") err = os.WriteFile(outputZip, zipData, 0644) if err != nil { return fmt.Errorf("failed to write decrypted zip file: %w", err) } return nil } func loadPrivateKey(path string) (*rsa.PrivateKey, error) { data, err := os.ReadFile(path) if err != nil { return nil, err } block, _ := pem.Decode(data) if block == nil || block.Type != "PRIVATE KEY" { return nil, errors.New("invalid private key format") } priv, err := x509.ParsePKCS8PrivateKey(block.Bytes) if err != nil { return nil, err } return priv.(*rsa.PrivateKey), nil } func decryptAES(cipherData, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(block) if err != nil { return nil, err } nonceSize := gcm.NonceSize() if len(cipherData) < nonceSize { return nil, fmt.Errorf("cipher data too short") } nonce, cipherText := cipherData[:nonceSize], cipherData[nonceSize:] return gcm.Open(nil, nonce, cipherText, nil) }