This repository has been archived on 2021-07-08. You can view files and clone it, but cannot push or open issues or pull requests.
opensend/fileCrypto.go

149 lines
5.7 KiB
Go

package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/md5"
"crypto/rand"
"encoding/hex"
"github.com/klauspost/compress/zstd"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"io"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
)
// Encrypt given file using the shared key
func CompressAndEncryptFile(filePath string, newFilePath string, sharedKey string) {
// Use ConsoleWriter logger
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
// Read data from file
file, err := os.Open(filePath)
if err != nil { log.Fatal().Err(err).Msg("Error opening file") }
// Create buffer for compressed data
compressedBuffer := new(bytes.Buffer)
// Create Zstd encoder
zstdEncoder, err := zstd.NewWriter(compressedBuffer)
if err != nil { log.Fatal().Err(err).Msg("Error creating Zstd encoder") }
// Copy file data to Zstd encoder
_, err = io.Copy(zstdEncoder, file)
if err != nil { log.Fatal().Err(err).Msg("Error reading file") }
// Close Zstd encoder
zstdEncoder.Close()
// Read compressed data into data variable
data, err := ioutil.ReadAll(compressedBuffer)
if err != nil { log.Fatal().Err(err).Msg("Error reading compressed buffer") }
// Create md5 hash of password in order to make it the required size
md5Hash := md5.New()
md5Hash.Write([]byte(sharedKey))
// Encode md5 hash bytes into hexadecimal
hashedKey := hex.EncodeToString(md5Hash.Sum(nil))
// Create new AES cipher
block, err := aes.NewCipher([]byte(hashedKey))
if err != nil { log.Fatal().Err(err).Msg("Error creating AES cipher") }
// Create GCM for AES cipher
gcm, err := cipher.NewGCM(block)
if err != nil { log.Fatal().Err(err).Msg("Error creating GCM") }
// Make byte slice for nonce
nonce := make([]byte, gcm.NonceSize())
// Read random bytes into nonce slice
_, err = io.ReadFull(rand.Reader, nonce)
if err != nil { log.Fatal().Err(err).Msg("Error creating nonce") }
// Encrypt data
ciphertext := gcm.Seal(nonce, nonce, data, nil)
// Create new file
newFile, err := os.Create(newFilePath)
if err != nil { log.Fatal().Err(err).Msg("Error creating file") }
// Defer file close
defer newFile.Close()
// Write ciphertext to new file
bytesWritten, err := newFile.Write(ciphertext)
if err != nil { log.Fatal().Err(err).Msg("Error writing to file") }
// Log bytes written and to which file
log.Info().Str("file", filepath.Base(newFilePath)).Msg("Wrote " + strconv.Itoa(bytesWritten) + " bytes")
}
// Decrypt given file using the shared key
func DecryptAndDecompressFile(filePath string, newFilePath string, sharedKey string) {
// Use ConsoleWriter logger
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
// Read data from file
data, err := ioutil.ReadFile(filePath)
if err != nil { log.Fatal().Err(err).Msg("Error reading file") }
// Create md5 hash of password in order to make it the required size
md5Hash := md5.New()
md5Hash.Write([]byte(sharedKey))
hashedKey := hex.EncodeToString(md5Hash.Sum(nil))
// Create new AES cipher
block, _ := aes.NewCipher([]byte(hashedKey))
// Create GCM for AES cipher
gcm, err := cipher.NewGCM(block)
if err != nil { log.Fatal().Err(err).Msg("Error creating GCM") }
// Get standard GCM nonce size
nonceSize := gcm.NonceSize()
// Get nonce and ciphertext from data
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
// Decrypt data
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil { log.Fatal().Err(err).Msg("Error decrypting data") }
// Create new Zstd decoder
zstdDecoder, err := zstd.NewReader(bytes.NewBuffer(plaintext))
if err != nil { log.Fatal().Err(err).Msg("Error creating Zstd decoder") }
// Create new file
newFile, err := os.Create(newFilePath)
if err != nil { log.Fatal().Err(err).Msg("Error creating file") }
// Close new file at the end of this function
defer newFile.Close()
// Write decompressed plaintext to new file
bytesWritten, err := io.Copy(newFile, zstdDecoder)
if err != nil { log.Fatal().Err(err).Msg("Error writing to file") }
zstdDecoder.Close()
// Log bytes written and to which file
log.Info().Str("file", filepath.Base(newFilePath)).Msg("Wrote " + strconv.Itoa(int(bytesWritten)) + " bytes")
}
// Encrypt files in given directory using shared key
func EncryptFiles(dir string, sharedKey string) {
// Use ConsoleWriter logger
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
// Walk given directory
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
// If error reading, return err
if err != nil { return err }
// If file is not a directory and is not the key
if !info.IsDir() && !strings.Contains(path, "key.aes"){
// Compress and Encrypt the file using shared key, appending .zst.enc
CompressAndEncryptFile(path, path + ".zst.enc", sharedKey)
// Remove unencrypted file
err := os.Remove(path)
if err != nil { return err }
}
// Return nil if no error occurs
return nil
})
if err != nil { log.Fatal().Err(err).Msg("Error encrypting files") }
}
// Decrypt files in given directory using shared key
func DecryptFiles(dir string, sharedKey string) {
// Use ConsoleWriter logger
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
// Walk given directory
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
// If error reading, return err
if err != nil { return err }
// If file is not a directory and is encrypted
if !info.IsDir() && strings.Contains(path, ".enc") {
// Decrypt and decompress the file using the shared key, removing .zst.enc
DecryptAndDecompressFile(path, strings.TrimSuffix(path, ".zst.enc"), sharedKey)
}
// Return nil if no errors occurred
return nil
})
if err != nil { log.Fatal().Err(err).Msg("Error decrypting files") }
}