Skip to content

Instantly share code, notes, and snippets.

@rueian
Last active January 7, 2024 14:39
Show Gist options
  • Save rueian/d6db48f7eeca70d70d30522c28b1963d to your computer and use it in GitHub Desktop.
Save rueian/d6db48f7eeca70d70d30522c28b1963d to your computer and use it in GitHub Desktop.
package main
import (
"archive/tar"
"compress/gzip"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"io"
"log"
"os"
"path/filepath"
)
// createTarGz creates a .tar.gz file at targetTarGzPath from the contents of srcDirPath.
func createTarGz(targetTarGzPath, srcDirPath string, key []byte) error {
// Create output file
tarGzFile, err := os.Create(targetTarGzPath)
if err != nil {
return err
}
defer tarGzFile.Close()
block, err := aes.NewCipher(key)
if err != nil {
return err
}
iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return err
}
// Write the IV at the beginning of the file
if _, err := tarGzFile.Write(iv); err != nil {
return err
}
// Create the stream encrypter
stream := cipher.NewCFBEncrypter(block, iv)
writer := &cipher.StreamWriter{S: stream, W: tarGzFile}
defer writer.Close()
// Gzip writer
gzipWriter := gzip.NewWriter(writer)
defer gzipWriter.Close()
// Tar writer
tarWriter := tar.NewWriter(gzipWriter)
defer tarWriter.Close()
// Walk through the source directory
err = filepath.Walk(srcDirPath, func(file string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
// Create a new dir/file header
header, err := tar.FileInfoHeader(fi, fi.Name())
if err != nil {
return err
}
relPath, err := filepath.Rel(srcDirPath, file)
if err != nil {
return err
}
header.Name = filepath.ToSlash(relPath)
if header.Name == "." && !fi.IsDir() {
header.Name = filepath.Base(file)
}
if err := tarWriter.WriteHeader(header); err != nil {
return err
}
if !fi.IsDir() {
file, err := os.Open(file)
if err != nil {
return err
}
defer file.Close()
if _, err := io.Copy(tarWriter, file); err != nil {
return err
}
}
return nil
})
return err
}
// untar extracts a .tar.gz file to a target directory.
func untar(tarGzPath, targetDir string, key []byte) error {
// Open the tar.gz file
file, err := os.Open(tarGzPath)
if err != nil {
return err
}
defer file.Close()
// Read the IV from the beginning of the file
iv := make([]byte, aes.BlockSize)
if _, err := io.ReadFull(file, iv); err != nil {
return err
}
// Create the AES cipher
block, err := aes.NewCipher(key)
if err != nil {
return err
}
// Create the stream decrypter
stream := cipher.NewCFBDecrypter(block, iv)
// Create a reader to read the decrypted data
reader := &cipher.StreamReader{S: stream, R: file}
// Gzip reader
gzipReader, err := gzip.NewReader(reader)
if err != nil {
return err
}
defer gzipReader.Close()
// Tar reader
tarReader := tar.NewReader(gzipReader)
// Iterate through the files in the archive.
for {
header, err := tarReader.Next()
if err == io.EOF {
break // End of archive
}
if err != nil {
return err
}
// Target for untarred files
target := filepath.Join(targetDir, header.Name)
switch header.Typeflag {
case tar.TypeDir: // = directory
// Create directory
if err := os.MkdirAll(target, 0755); err != nil {
return err
}
case tar.TypeReg: // = regular file
// Create file
outFile, err := os.Create(target)
if err != nil {
return err
}
// Copy file data from tar
if _, err := io.Copy(outFile, tarReader); err != nil {
outFile.Close()
return err
}
outFile.Close()
}
}
return nil
}
func main() {
key := make([]byte, 32) // AES-256 requires a 32-byte key
_, err := rand.Read(key)
if err != nil {
log.Fatal(err)
}
// Usage example
err = createTarGz("/go/loader/test/test.tar.gz.enc", "/go/loader/cmd", key)
if err != nil {
log.Fatal(err)
}
err = untar("/go/loader/test/test.tar.gz.enc", "/go/loader/test", key)
if err != nil {
log.Fatal(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment