130 lines
2.6 KiB
Go
130 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"image"
|
|
"log"
|
|
"os"
|
|
|
|
"github.com/auyer/steganography"
|
|
"github.com/chai2010/webp"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
type DecodeConfig struct {
|
|
Security struct {
|
|
AESKey string `yaml:"aes_key"` // Base64 encoded 32-byte key
|
|
} `yaml:"security"`
|
|
}
|
|
|
|
var (
|
|
aesKey []byte
|
|
cfg DecodeConfig
|
|
)
|
|
|
|
func loadDecodeConfig(filename string) error {
|
|
data, err := os.ReadFile(filename)
|
|
if err != nil {
|
|
return fmt.Errorf("config file read error: %w", err)
|
|
}
|
|
|
|
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
|
return fmt.Errorf("config parse error: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func decryptData(ciphertext []byte) ([]byte, error) {
|
|
block, err := aes.NewCipher(aesKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
gcm, err := cipher.NewGCM(block)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nonceSize := gcm.NonceSize()
|
|
if len(ciphertext) < nonceSize {
|
|
return nil, fmt.Errorf("ciphertext too short")
|
|
}
|
|
|
|
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
|
|
return gcm.Open(nil, nonce, ciphertext, nil)
|
|
}
|
|
|
|
func decodeImage(file *os.File) (image.Image, error) {
|
|
reader := bufio.NewReader(file)
|
|
|
|
// Try to decode with image.Decode first
|
|
img, _, err := image.Decode(reader)
|
|
if err == nil {
|
|
return img, nil
|
|
}
|
|
|
|
// Try fallback: WebP
|
|
// Reset file offset to 0
|
|
_, _ = file.Seek(0, 0)
|
|
img, err = webp.Decode(file)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unsupported image format or decode failure: %w", err)
|
|
}
|
|
return img, nil
|
|
}
|
|
|
|
func main() {
|
|
if len(os.Args) < 2 {
|
|
fmt.Println("Usage: go run decode.go <encoded_image>")
|
|
return
|
|
}
|
|
|
|
if err := loadDecodeConfig("config.yaml"); err != nil {
|
|
log.Fatalf("Failed to load config: %v", err)
|
|
}
|
|
|
|
filePath := os.Args[1]
|
|
|
|
inFile, err := os.Open(filePath)
|
|
if err != nil {
|
|
fmt.Printf("Failed to open file: %v\n", err)
|
|
return
|
|
}
|
|
defer inFile.Close()
|
|
|
|
// Initialize AES key
|
|
if cfg.Security.AESKey != "" {
|
|
aesKey, err = base64.StdEncoding.DecodeString(cfg.Security.AESKey)
|
|
if err != nil {
|
|
log.Fatalf("Invalid AES key in config: %v", err)
|
|
}
|
|
} else {
|
|
log.Fatal("AES key not provided in config.")
|
|
}
|
|
|
|
img, err := decodeImage(inFile)
|
|
if err != nil {
|
|
fmt.Printf("Failed to decode image: %v\n", err)
|
|
return
|
|
}
|
|
|
|
size := steganography.GetMessageSizeFromImage(img)
|
|
message := steganography.Decode(size, img)
|
|
if message == nil {
|
|
fmt.Println("No message found in the image.")
|
|
return
|
|
}
|
|
|
|
decryptedMessage, err := decryptData(message)
|
|
if err != nil {
|
|
fmt.Printf("Failed to decrypt message: %v\n", err)
|
|
return
|
|
}
|
|
|
|
fmt.Printf("Decrypted message: %s\n", decryptedMessage)
|
|
}
|