1
0
Fork 0
mirror of https://github.com/X-Cli/large-file-decrypt.git synced 2024-09-19 19:52:09 +00:00
large-file-crypto/encrypt/encrypt.go
Florian Maury b1f60357fa initial
2022-01-10 12:21:33 +01:00

102 lines
3.4 KiB
Go

package encrypt
import (
"crypto/rand"
"fmt"
"io"
"path/filepath"
"syscall"
"github.com/X-Cli/large-file-decrypt/utils"
"github.com/hashicorp/go-multierror"
"golang.org/x/crypto/nacl/box"
"golang.org/x/sys/unix"
)
func EncryptFile(inputPath, outputPath string, pubKey *[32]byte) error {
return encryptFile(inputPath, outputPath, pubKey, rand.Reader).ErrorOrNil()
}
func encryptFile(inputPath, outputPath string, pubKey *[32]byte, cryptoReader io.Reader) (errStack *multierror.Error) {
inputTempDir := filepath.Dir(inputPath)
privateClearFile, errs := utils.CreatePrivateCopyOf(inputPath, inputTempDir)
if errs.ErrorOrNil() != nil {
errStack = multierror.Append(errStack, fmt.Errorf("failed to acquire a private copy of %q: %w", inputPath, errs))
return
}
privateClearFileClosed := false
defer func() {
if !privateClearFileClosed {
if err := privateClearFile.Close(); err != nil {
errStack = multierror.Append(errStack, fmt.Errorf("failed to close private copy of %q: %w", inputPath, err))
}
}
}()
clearData, err := utils.GetDataFromFile(privateClearFile)
if err != nil {
errStack = multierror.Append(errStack, fmt.Errorf("failed to get data from private copy: %w", err))
return
}
clearDataFreed := false
defer func() {
if !clearDataFreed {
if err := syscall.Munmap(clearData); err != nil {
errStack = multierror.Append(errStack, fmt.Errorf("failed to munmap clear data: %w", err))
}
}
}()
outputTempDir := filepath.Dir(outputPath)
encryptedSize := len(clearData) + box.AnonymousOverhead
privateEncryptedFile, err := utils.CreatePrivateFile(outputTempDir, int64(encryptedSize))
if err != nil {
errStack = multierror.Append(errStack, fmt.Errorf("failed to create private encrypted file: %w", err))
return
}
defer func() {
if err := privateEncryptedFile.Close(); err != nil {
errStack = multierror.Append(errStack, fmt.Errorf("failed to close private encrypted file: %w", err))
}
}()
encryptedData, err := utils.GetDataFromFile(privateEncryptedFile)
if err != nil {
errStack = multierror.Append(errStack, fmt.Errorf("failed to get data from encrypted file: %w", err))
return
}
defer func() {
if err := syscall.Munmap(encryptedData); err != nil {
errStack = multierror.Append(errStack, fmt.Errorf("failed to release encrypted data: %w", err))
}
}()
if _, err := box.SealAnonymous(encryptedData[:0], clearData, pubKey, cryptoReader); err != nil {
errStack = multierror.Append(errStack, fmt.Errorf("failed to seal data: %w", err))
return
}
if err := unix.Msync(encryptedData, unix.MS_SYNC); err != nil {
errStack = multierror.Append(errStack, fmt.Errorf("failed to flush encrypted data on disk: %w", err))
return
}
// Releasing private clear text resources since they are no longer needed and they may occupy resources if file clone was not possible
clearDataFreed = true
if err := syscall.Munmap(clearData); err != nil {
errStack = multierror.Append(errStack, fmt.Errorf("failed to get data from private copy: %w", err))
return
}
privateClearFileClosed = true
if err := privateClearFile.Close(); err != nil {
errStack = multierror.Append(errStack, fmt.Errorf("failed to close private copy of %q: %w", inputPath, err))
return
}
if err := utils.PublishFile(privateEncryptedFile, outputPath); err.ErrorOrNil() != nil {
errStack = multierror.Append(errStack, fmt.Errorf("failed to publish encrypted file: %w", err))
return
}
return
}