2025-04-25 15:30:01 +02:00

157 lines
3.3 KiB
Go

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)
}