Skip to content

Commit

Permalink
Re-implemented SharpAESCrypt
Browse files Browse the repository at this point in the history
  • Loading branch information
kenkendk committed Jun 4, 2024
1 parent 8f8fdb9 commit faeaf5e
Show file tree
Hide file tree
Showing 20 changed files with 2,398 additions and 0 deletions.
45 changes: 45 additions & 0 deletions .github/workflows/run-tests-on-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Run tests on PR

on: [push]

jobs:
build-and-deploy:
runs-on: ubuntu-latest
strategy:
matrix:
dotnet-version: ["8.0.x"]

steps:
- name: Checkout master
uses: actions/checkout@main

- name: Setup .NET Core
uses: actions/setup-dotnet@main
with:
dotnet-version: ${{ matrix.dotnet-version }}

- name: Setup caching Nuget packages
uses: actions/cache@main
with:
path: ~/.nuget/packages
# Look to see if there is a cache hit for the corresponding requirements file
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: |
${{ runner.os }}-nuget
- name: Install dependencies
run: dotnet restore

- name: Build
run: dotnet build --no-restore

- name: Test with the dotnet CLI
run: dotnet test --no-build --logger trx --results-directory "TestResults-${{ matrix.dotnet-version }}"

- name: Upload dotnet test results
uses: actions/upload-artifact@v4
with:
name: dotnet-results-${{ matrix.dotnet-version }}
path: TestResults-${{ matrix.dotnet-version }}
# Use always() to always run this step to publish test results when there are test failures
if: ${{ always() }}
72 changes: 72 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# SharpAESCrypt

A C# implementation of the [AESCrypt file format](https://www.aescrypt.com/).

This .NET AES Crypt package contains the C# class `SharpAESCrypt.SharpAESCrypt`, which provides file encryption and decryption using the [aescrypt file format](https://www.aescrypt.com/aes_file_format.html).

Version 2 of the AES File Format is supported for reading and writing. Versions 0 and 1 are not verified, but there is code to read and write the formats.

# Downloads

You can [install SharpAESCrypt from NuGet](https://www.nuget.org/packages/SharpAESCrypt).

The library is targeting .NET8. For [versions supporting Mono and .NET4, use v1.3.4](https://www.nuget.org/packages/SharpAESCrypt.dll/1.3.4) with a [different codebase](https://github.com/kenkendk/sharpaescrypt).

# Usage

With a reference to `SharpAESCrypt`, the primary interface are static methods:

```C#
using SharpAESCrypt;
AESCrypt.Encrypt("password", "inputfile", "outputfile");
AESCrypt.Decrypt("password", "inputfile", "outputfile");
AESCrypt.Encrypt("password", inputStream, outputStream);
AESCrypt.Decrypt("password", inputStream, outputStream);
```

For uses where a stream is required/prefered, streams can also be created by wrapping either output or input:

```C#
var encStream = new EncryptingStream(password, outputStream);
var decStream = new DecryptingStream(password, inputStream);
```

Remember to either call `Dispose()` or `FlushFinalBlock()` after using the stream.

# Options

Generally, it is recommended that only the default options are applied, but it is possible to toggle some options via the optional `options` parameter.

For encrypting, you can control the written fileversion and what headers to include (if using v2):

```C#
var options = new EncryptionOptions(
InsertCreatedByIdentifier: true,
InsertTimeStamp: true,
InsertPlaceholder: true,
FileVersion: AESCrypt.DEFAULT_FILE_VERSION,
LeaveOpen: false,
AdditionalExtensions = new Dictionary<string, byte[]> {
{ "aes", new byte[] { 0x41, 0x45, 0x53 } }
}
);

SharpAESCrypt.Encrypt("password", "inputfile", "outputfile", options);
```

For decrypting you can toggle some compatibility options:

```C#
var options = new DecyptionOptions(
MinVersion: 2,
LeaveOpen: false,
IgnorePaddingBytes: false,
IgnoreFileLength: false
);

SharpAESCrypt.Decrypt("password", "inputfile", "outputfile"), options;
```

The option `IgnorePaddingBytes` can be set to `true` to skip a consistency check made by this library.
The consistency check counters a [length modification vulnerability in the original format](https://www.aescrypt.com/wishlist.html).
If you need to read files generated by another tool, you may need to toggle this option.
28 changes: 28 additions & 0 deletions SharpAESCrypt.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpAESCrypt", "src\SharpAESCrypt.csproj", "{6C506B87-ED2E-435D-91E7-80E36CC96BDA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unittest", "test\Unittest.csproj", "{92A56A7D-DAA1-4A2E-835A-CAB693B66E9D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6C506B87-ED2E-435D-91E7-80E36CC96BDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6C506B87-ED2E-435D-91E7-80E36CC96BDA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6C506B87-ED2E-435D-91E7-80E36CC96BDA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6C506B87-ED2E-435D-91E7-80E36CC96BDA}.Release|Any CPU.Build.0 = Release|Any CPU
{92A56A7D-DAA1-4A2E-835A-CAB693B66E9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{92A56A7D-DAA1-4A2E-835A-CAB693B66E9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{92A56A7D-DAA1-4A2E-835A-CAB693B66E9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{92A56A7D-DAA1-4A2E-835A-CAB693B66E9D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
129 changes: 129 additions & 0 deletions src/AESCrypt.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
namespace SharpAESCrypt;

/// <summary>
/// Provides an API for AESCrypt encryption and decryption.
/// </summary>
/// <remarks>
/// The file format declare support for 2^64 bytes encrypted data, but .Net has trouble
/// with files more than 2^63 bytes long, so this module 'only' supports 2^63 bytes
/// (long vs ulong).
/// </remarks>
public static class AESCrypt
{
/// <summary>
/// The default file format options to use
/// </summary>
public const byte DEFAULT_FILE_VERSION = 2;

/// <summary>
/// Encrypts a stream using the supplied password
/// </summary>
/// <param name="password">The password to decrypt with</param>
/// <param name="input">The stream with unencrypted data</param>
/// <param name="output">The encrypted output stream</param>
/// <param name="options">The encryption options to use</param>
public static void Encrypt(string password, Stream input, Stream output, EncryptionOptions? options = default)
{
using var c = new EncryptingStream(password, output, options);
input.CopyTo(c);
c.FlushFinalBlock();
}

/// <summary>
/// Encrypts a stream using the supplied password
/// </summary>
/// <param name="password">The password to decrypt with</param>
/// <param name="input">The stream with unencrypted data</param>
/// <param name="output">The encrypted output stream</param>
/// <param name="options">The encryption options to use</param>
public static async Task EncryptAsync(string password, Stream input, Stream output, EncryptionOptions? options = default, CancellationToken ct = default)
{
using var c = new EncryptingStream(password, output, options);
await input.CopyToAsync(output, ct);
c.FlushFinalBlock();
}

/// <summary>
/// Decrypts a stream using the supplied password
/// </summary>
/// <param name="password">The password to encrypt with</param>
/// <param name="input">The stream with encrypted data</param>
/// <param name="output">The unencrypted output stream</param>
/// <param name="options">The decryption options to use</param>
public static void Decrypt(string password, Stream input, Stream output, DecryptionOptions? options = default)
{
using var c = new DecryptingStream(password, input, options);
c.CopyTo(output);
}

/// <summary>
/// Decrypts a stream using the supplied password
/// </summary>
/// <param name="password">The password to encrypt with</param>
/// <param name="input">The stream with encrypted data</param>
/// <param name="output">The unencrypted output stream</param>
/// <param name="options">The decryption options to use</param>
public static async Task DecryptAsync(string password, Stream input, Stream output, DecryptionOptions? options = default, CancellationToken ct = default)
{
using var c = new DecryptingStream(password, input, options);
await c.CopyToAsync(output, ct);
}

/// <summary>
/// Encrypts a file using the supplied password
/// </summary>
/// <param name="password">The password to encrypt with</param>
/// <param name="inputfile">The file with unencrypted data</param>
/// <param name="outputfile">The encrypted output file</param>
/// <param name="maxThreads">Maximum threads allowed for SharpAESCrypt. </param>
/// <param name="options">The encryption options to use</param>
public static void Encrypt(string password, string inputfile, string outputfile, EncryptionOptions? options = default)
{
using (FileStream infs = File.OpenRead(inputfile))
using (FileStream outfs = File.Create(outputfile))
Encrypt(password, infs, outfs, options);
}

/// <summary>
/// Encrypts a file using the supplied password
/// </summary>
/// <param name="password">The password to encrypt with</param>
/// <param name="inputfile">The file with unencrypted data</param>
/// <param name="outputfile">The encrypted output file</param>
/// <param name="maxThreads">Maximum threads allowed for SharpAESCrypt. </param>
/// <param name="options">The encryption options to use</param>
public static async Task EncryptAsync(string password, string inputfile, string outputfile, EncryptionOptions? options = default)
{
using (FileStream infs = File.OpenRead(inputfile))
using (FileStream outfs = File.Create(outputfile))
await EncryptAsync(password, infs, outfs, options);
}

/// <summary>
/// Decrypts a file using the supplied password
/// </summary>
/// <param name="password">The password to decrypt with</param>
/// <param name="inputfile">The file with encrypted data</param>
/// <param name="outputfile">The unencrypted output file</param>
/// <param name="options">The decryption options to use</param>
public static void Decrypt(string password, string inputfile, string outputfile, DecryptionOptions? options = default)
{
using (FileStream infs = File.OpenRead(inputfile))
using (FileStream outfs = File.Create(outputfile))
Decrypt(password, infs, outfs, options);
}

/// <summary>
/// Decrypts a file using the supplied password
/// </summary>
/// <param name="password">The password to decrypt with</param>
/// <param name="inputfile">The file with encrypted data</param>
/// <param name="outputfile">The unencrypted output file</param>
/// <param name="options">The decryption options to use</param>
public static async Task DecryptAsync(string password, string inputfile, string outputfile, DecryptionOptions? options = default)
{
using (FileStream infs = File.OpenRead(inputfile))
using (FileStream outfs = File.Create(outputfile))
await DecryptAsync(password, infs, outfs, options);
}
}
Loading

0 comments on commit faeaf5e

Please sign in to comment.