mirror of
https://github.com/veracrypt/VeraCrypt
synced 2024-11-24 03:53:29 +01:00
251 lines
8.4 KiB
PowerShell
251 lines
8.4 KiB
PowerShell
|
<#
|
||
|
.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."
|