Sling Academy
Home/Golang/Encrypting Large Files with Chunk-Based AES Encryption in Go

Encrypting Large Files with Chunk-Based AES Encryption in Go

Last updated: November 27, 2024

When dealing with large files, encrypting them efficiently is crucial due to memory usage and performance considerations. One practical solution involves chunk-based encryption using Advanced Encryption Standard (AES) in the Go programming language. This technique allows you to handle files piece by piece rather than loading them into memory entirely.

1. Why Use Chunk-Based Encryption?

First, let's consider the advantages of chunk-based encryption:

  • Memory Efficiency: By encrypting in chunks, the program avoids loading the entire file into memory, making it possible to encrypt or decrypt even very large files.
  • Parallel Processing: You can potentially encrypt/decrypt different chunks in parallel, improving performance.

2. Setting Up AES Encryption

To begin, ensure you have Go installed and you can create a new project. You'll need the crypto/aes and crypto/cipher libraries for AES encryption. Start by importing necessary packages:

package main

import (
  "crypto/aes"
  "crypto/cipher"
  "crypto/rand"
  "io"
  "os"
  "fmt"
)

Next, we will define a function to generate a new AES key where the key length can be 16, 24, or 32 bytes for AES-128, AES-192, or AES-256, respectively:

func generateKey() ([]byte, error) {
  key := make([]byte, 32) // AES-256
  _, err := rand.Read(key)
  if err != nil {
    return nil, err
  }
  return key, nil
}

3. AES Encryption and Decryption of File Chunks

Let's implement the core encryption and decryption functions for handling chunks of data.

3.1 Encrypting Chunks

Here is a function to encrypt a chunk:

func encryptChunk(block cipher.Block, inputData []byte) ([]byte, error) {
  ciphertext := make([]byte, len(inputData))
  iv := make([]byte, aes.BlockSize)
  if _, err := io.ReadFull(rand.Reader, iv); err != nil {
    return nil, err
  }

  mode := cipher.NewCFBEncrypter(block, iv)
  mode.XORKeyStream(ciphertext, inputData)
  return append(iv, ciphertext...), nil
}

3.2 Decrypting Chunks

And now for decryption:

func decryptChunk(block cipher.Block, inputData []byte) ([]byte, error) {
  if len(inputData) < aes.BlockSize {
    return nil, fmt.Errorf("ciphertext too short")
  }

  iv := inputData[:aes.BlockSize]
  ciphertext := inputData[aes.BlockSize:]

  mode := cipher.NewCFBDecrypter(block, iv)
  plaintext := make([]byte, len(ciphertext))
  mode.XORKeyStream(plaintext, ciphertext)

  return plaintext, nil
}

4. Processing a Large File in Chunks

This section explains how to read a file in chunks, encrypt each one, and write back to a new file. Similar logic applies during decryption:

func processFileChunks(filename string, outputFilename string, chunkSize int, key []byte, encrypt bool) error {
  file, err := os.Open(filename)
  if err != nil {
    return err
  }
  defer file.Close()

  outputFile, err := os.Create(outputFilename)
  if err != nil {
    return err
  }
  defer outputFile.Close()

  block, err := aes.NewCipher(key)
  if err != nil {
    return err
  }

  buffer := make([]byte, chunkSize)
  for {
    bytesRead, err := file.Read(buffer)
    if err != nil && err != io.EOF {
      return err
    }
    if bytesRead == 0 {
      break
    }
    buffer = buffer[:bytesRead]

    var processedData []byte
    if encrypt {
      processedData, err = encryptChunk(block, buffer)
    } else {
      processedData, err = decryptChunk(block, buffer)
    }
    if err != nil {
      return err
    }

    if _, err := outputFile.Write(processedData); err != nil {
      return err
    }
  }
  return nil
}

Depending on the parameter encrypt, the function will either encrypt or decrypt each chunk.

5. Conclusion

Chunk-based file encryption helps manage large files without exhausting system memory, allowing for efficient parallel processing and improved performance. By adapting the provided Go code snippets, you can implement a robust encryption mechanism suitable for various applications.

Next Article: Understanding and Preventing Padding Oracle Attacks in Go

Previous Article: Creating Secure Random UUIDs in Go

Series: Cryptography and Security in Go

Golang

Related Articles

You May Also Like

  • How to remove HTML tags in a string in Go
  • How to remove special characters in a string in Go
  • How to remove consecutive whitespace in a string in Go
  • How to count words and characters in a string in Go
  • Relative imports in Go: Tutorial & Examples
  • How to run Python code with Go
  • How to generate slug from title in Go
  • How to create an XML sitemap in Go
  • How to redirect in Go (301, 302, etc)
  • Using Go with MongoDB: CRUD example
  • Auto deploy Go apps with CI/ CD and GitHub Actions
  • Fixing Go error: method redeclared with different receiver type
  • Fixing Go error: copy argument must have slice type
  • Fixing Go error: attempted to use nil slice
  • Fixing Go error: assignment to constant variable
  • Fixing Go error: cannot compare X (type Y) with Z (type W)
  • Fixing Go error: method has pointer receiver, not called with pointer
  • Fixing Go error: assignment mismatch: X variables but Y values
  • Fixing Go error: array index must be non-negative integer constant