Windows: Add a PowerShell script that can be used to create a volume from a given directory or file

The script will try to calculate the minimal required size to store the given directory or file in the exFAT formatted volume
This commit is contained in:
Mounir IDRASSI 2023-07-07 23:55:21 +02:00
parent 26fffec1e3
commit 210968bed1
No known key found for this signature in database
GPG Key ID: 02C30AE90FAE4A6F
1 changed files with 250 additions and 0 deletions

250
contrib/EncryptData.ps1 Normal file
View File

@ -0,0 +1,250 @@
<#
.SYNOPSIS
This PowerShell script is used to create a VeraCrypt container with minimal size to hold a copy of the given input file or directory.
.DESCRIPTION
This script takes as input a file path or directory path and a container path.
If the container path is not specified, it defaults to the same as the input path with a ".hc" extension.
The script calculates the minimal size needed to hold the input file or directory in a VeraCrypt container.
It then creates a VeraCrypt container with the specified path and the calculated size using exFAT filesystem.
Finally, the container is mounted, the input file or directory is copied to the container and the container is dismounted.
.PARAMETER inputPath
The file path or directory path to be encrypted in the VeraCrypt container.
.PARAMETER containerPath
The desired path for the VeraCrypt container. If not specified, it defaults to the same as the input path with a ".hc" extension.
.EXAMPLE
.\EncryptData.ps1 -inputPath "C:\MyFolder" -containerPath "D:\MyContainer.hc"
.\EncryptData.ps1 "C:\MyFolder" "D:\MyContainer.hc"
.\EncryptData.ps1 "C:\MyFolder"
.NOTES
Author: Mounir IDRASSI
Email: mounir.idrassi@idrix.fr
Date: July 2023
License: This script is licensed under the Apache License 2.0
#>
# parameters
param(
[Parameter(Mandatory=$true)]
[string]$inputPath,
[string]$containerPath
)
function ConvertTo-AbsolutePath {
param (
[Parameter(Mandatory=$true)]
[string]$Path
)
if ([System.IO.Path]::IsPathRooted($Path)) {
return $Path
}
return Join-Path -Path (Get-Location) -ChildPath $Path
}
# Convert input path to fully qualified path
$inputPath = ConvertTo-AbsolutePath -Path $inputPath
# Check if input path exists
if (-not (Test-Path $inputPath)) {
Write-Host "The specified input path does not exist. Please provide a valid input path."
exit 1
}
$inputPath = (Resolve-Path -Path $inputPath).Path
# Set container path if not specified
if ([string]::IsNullOrWhiteSpace($containerPath)) {
$containerPath = "${inputPath}.hc"
} else {
$containerPath = ConvertTo-AbsolutePath -Path $containerPath
}
# Check if container path already exists
if (Test-Path $containerPath) {
Write-Host "The specified container path already exists. Please provide a unique path for the new container."
exit 1
}
# Full path to VeraCrypt executables
$veracryptPath = "C:\Program Files\VeraCrypt" # replace with your actual path
$veraCryptExe = Join-Path $veracryptPath "VeraCrypt.exe"
$veraCryptFormatExe = Join-Path $veracryptPath "VeraCrypt Format.exe"
# Constants used to calculate the size of the exFAT filesystem
$InitialVBRSize = 32KB
$InitialFATSize = 128KB
$ClusterSize = 32KB # TODO : make this configurable
function Get-ExFATSizeRec {
param(
[string]$Path,
[uint64] $TotalSize
)
# Constants
$BaseMetadataSize = 32
$DirectoryEntrySize = 32
try {
# Get the item (file or directory) at the provided path
$item = Get-Item -Path $Path -ErrorAction Stop
# Calculate metadata size
$fileNameLength = $item.Name.Length
$metadataSize = $BaseMetadataSize + ($fileNameLength * 2)
# Calculate directory entries
if ($fileNameLength -gt 15) {
$numDirEntries = [math]::Ceiling($fileNameLength / 15) + 1
} else {
$numDirEntries = 2
}
$dirEntriesSize = $numDirEntries * $DirectoryEntrySize
# Add metadata, file size, and directory entries size to $TotalSize
$TotalSize += $metadataSize + $dirEntriesSize
if ($item.PSIsContainer) {
# It's a directory
$childItems = Get-ChildItem -Path $Path -ErrorAction Stop
foreach ($childItem in $childItems) {
# Recursively call this function for each child item
$TotalSize = Get-ExFATSizeRec -Path $childItem.FullName -TotalSize $TotalSize
}
} else {
# It's a file
# Calculate actual file size and round it up to the nearest multiple of $ClusterSize
$fileSize = $item.Length
$totalFileSize = [math]::Ceiling($fileSize / $ClusterSize) * $ClusterSize
# Add metadata, file size, and directory entries size to $TotalSize
$TotalSize += $totalFileSize
}
} catch {
Write-Error "Error processing item at path ${Path}: $_"
}
return $TotalSize
}
function Get-ExFATSize {
param(
[string]$Path
)
try {
# Initialize total size
$totalSize = $InitialVBRSize + $InitialFATSize
# Call the recursive function
$totalSize = Get-ExFATSizeRec -Path $Path -TotalSize $totalSize
# Add the root directory to $totalSize
$totalSize += $ClusterSize
# Calculate the size of the Bitmap Allocation Table
$numClusters = $totalSize / $ClusterSize
$bitmapSize = [math]::Ceiling($numClusters / 8)
$totalSize += $bitmapSize
# Adjust the size of the FAT
$fatSize = $numClusters * 4
$totalSize += $fatSize - $InitialFATSize
# Return the minimum disk size needed to store the exFAT filesystem
return $totalSize
} catch {
Write-Error "Error calculating exFAT size for path ${Path}: $_"
return 0
}
}
# Calculate size of the container
$containerSize = Get-ExFATSize -Path $inputPath
$containerSize = [math]::Ceiling($containerSize / 1MB)
# Add 1 MiB to account for the VeraCrypt headers and reserved areas (256 KiB), plus other overhead
$containerSize += 1
# Specify encryption algorithm, and hash algorithm
$encryption = "AES"
$hash = "sha512"
# Create a SecureString password
$password = Read-Host -AsSecureString -Prompt "Enter your password"
# Create a PSCredential object
$cred = New-Object System.Management.Automation.PSCredential ("username", $password)
Write-Host "Creating VeraCrypt container `"$containerPath`" ..."
# Create file container using VeraCrypt Format
# TODO: Add a switch to VeraCrypt Format to allow specifying the cluster size to use for the container
$veraCryptFormatArgs = "/create `"$containerPath`" /size `"${containerSize}M`" /password $($cred.GetNetworkCredential().Password) /encryption $encryption /hash $hash /filesystem `"exFAT`" /quick /silent"
Start-Process $veraCryptFormatExe -ArgumentList $veraCryptFormatArgs -NoNewWindow -Wait
# Check that the container was successfully created
if (-not (Test-Path $containerPath)) {
Write-Host "An error occurred while creating the VeraCrypt container."
exit 1
}
# Get a list of currently used drive letters
$driveLetter = Get-Volume | Where-Object { $_.DriveLetter -ne $null } | Select-Object -ExpandProperty DriveLetter
# Find the first available drive letter
$unusedDriveLetter = (70..90 | ForEach-Object { [char]$_ } | Where-Object { $_ -notin $driveLetter })[0]
# If no available drive letter was found, print an error message and exit the script
if ($null -eq $unusedDriveLetter) {
# delete the file container that was created
Remove-Item -Path $containerPath -Force
Write-Error "No available drive letters found. Please free up a drive letter and try again."
exit 1
}
Write-Host "Mounting the newly created VeraCrypt container..."
# Mount the container to the chosen drive letter as removable media
Start-Process $veraCryptExe -ArgumentList "/volume `"$containerPath`" /letter $unusedDriveLetter /m rm /password $($cred.GetNetworkCredential().Password) /quit" -NoNewWindow -Wait
# Check if the volume has been mounted successfully
$mountedDriveRoot = "${unusedDriveLetter}:\"
if (-not (Test-Path -Path $mountedDriveRoot)) {
# Volume mount failed
Write-Error "Failed to mount the volume. Please make sure VeraCrypt.exe is working correctly."
# delete the file container that was created
Remove-Item -Path $containerPath -Force
exit 1
}
Write-Host "Copying data to the mounted VeraCrypt container..."
# Copy the file or directory to the mounted drive
if (Test-Path -Path $inputPath -PathType Container) {
# For directories
Copy-Item -Path $inputPath -Destination "$($unusedDriveLetter):\" -Recurse
} else {
# For files
Copy-Item -Path $inputPath -Destination "$($unusedDriveLetter):\"
}
Write-Host "Copying completed. Dismounting the VeraCrypt container..."
# give some time for the file system to flush the data to the disk
Start-Sleep -Seconds 5
# Dismount the volume
Start-Process $veraCryptExe -ArgumentList "/dismount $unusedDriveLetter /quit" -NoNewWindow -Wait
Write-Host "VeraCrypt container created successfully."