diff --git a/.github/workflows/nuget_publish.yml b/.github/workflows/nuget_publish.yml
new file mode 100644
index 0000000..c1ab9bc
--- /dev/null
+++ b/.github/workflows/nuget_publish.yml
@@ -0,0 +1,61 @@
+name: Publish to nuget
+
+on:
+ push:
+ branches:
+ - master
+ paths:
+ - "**/*.cs"
+ - "**/*.csproj"
+
+jobs:
+
+ nuget_publish:
+ name: Build, pack and nuget publish
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v6
+ - uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: |
+ 6.0.x
+ 8.0.x
+
+ - name: Launch unit tests
+ run: dotnet test --nologo
+
+ - name: NuGet publish
+ id: nuget_worker
+ uses: alirezanet/publish-nuget@v3.1.0
+ with:
+ # Filepath of the project to be packaged, relative to root of repository
+ PROJECT_FILE_PATH: SQLCipherED/SQLCipherED.csproj
+
+ # NuGet package id, used for version detection & defaults to project name
+ # PACKAGE_NAME: AesIge
+
+ # Filepath with version info, relative to root of repository & defaults to PROJECT_FILE_PATH
+ # VERSION_FILE_PATH: Directory.Build.props
+
+ # Regex pattern to extract version info in a capturing group
+ # Version>([\d]+(.[\d]+)*)<\/\w*Version>$ - NET Core csproj regex
+ VERSION_REGEX: Version>([\d]+(.[\d]+)*)<\/\w*Version>$
+
+ # Useful with external providers like Nerdbank.GitVersioning, ignores VERSION_FILE_PATH & VERSION_REGEX
+ # VERSION_STATIC: 1.0.0
+
+ # Flag to toggle git tagging, enabled by default
+ TAG_COMMIT: false
+
+ # Format of the git tag, [*] gets replaced with actual version
+ # TAG_FORMAT: v*
+
+ # API key to authenticate with NuGet server
+ NUGET_KEY: ${{secrets.NUGET_API_KEY}}
+
+ # NuGet server uri hosting the packages, defaults to https://api.nuget.org
+ # NUGET_SOURCE: https://api.nuget.org
+
+ # Flag to toggle pushing symbols along with nuget package to the server, disabled by default
+ INCLUDE_SYMBOLS: false
\ No newline at end of file
diff --git a/.github/workflows/pr_unit_test.yml b/.github/workflows/pr_unit_test.yml
index 8477935..3b86045 100644
--- a/.github/workflows/pr_unit_test.yml
+++ b/.github/workflows/pr_unit_test.yml
@@ -17,8 +17,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-dotnet@v3
+ - uses: actions/checkout@v6
+ - uses: actions/setup-dotnet@v5
with:
dotnet-version: '6.x'
- name: Launch tests
diff --git a/.github/workflows/release_parallel.yml b/.github/workflows/release_parallel.yml
index 893f95b..cc7557a 100644
--- a/.github/workflows/release_parallel.yml
+++ b/.github/workflows/release_parallel.yml
@@ -18,12 +18,21 @@ jobs:
unit_tests:
name: Unit tests
runs-on: ubuntu-latest
+ outputs:
+ release_ver: ${{ steps.lib_proj_version.outputs.assembly-version }}
steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-dotnet@v3
+ - uses: actions/checkout@v6
+ - uses: actions/setup-dotnet@v5
with:
dotnet-version: '6.x'
+
+ - name: Parse lib csproj version
+ id: lib_proj_version
+ uses: kzrnm/get-net-sdk-project-versions-action@v2
+ with:
+ proj-path: SQLCipherED/SQLCipherED.csproj
+
- name: Launch tests
run: dotnet test --nologo
@@ -34,31 +43,22 @@ jobs:
env:
BUILD_ARCH: win-x64
BUILD_DIR: ./build/SQLCipherED.UI/Release/net6.0/win-x64
- BUILD_ARTIFACTS: SQLCipherED.dll runner-sqlcipher-ed.dll runner-sqlcipher-ed.exe runner-sqlcipher-ed.runtimeconfig.json
ZIP_FILE: sqlcipher-ed_win-x64.zip
- outputs:
- release_ver: ${{ steps.app_proj_version.outputs.assembly-version }}
steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-dotnet@v3
+ - uses: actions/checkout@v6
+ - uses: actions/setup-dotnet@v5
with:
dotnet-version: '6.x'
-
- - name: Parse app csproj version
- id: app_proj_version
- uses: kzrnm/get-net-sdk-project-versions-action@v1
- with:
- proj-path: SQLCipherED.UI/SQLCipherED.UI.csproj
- name: Build CLI app
- run: dotnet build SQLCipherED.UI/SQLCipherED.UI.csproj -r ${{env.BUILD_ARCH}} -c Release
+ run: dotnet build SQLCipherED.UI/SQLCipherED.UI.csproj -r ${{env.BUILD_ARCH}} -c Release --self-contained
- name: Zip build
- run: cd ${{env.BUILD_DIR}} && zip ${{env.ZIP_FILE}} ${{env.BUILD_ARTIFACTS}}
+ run: cd ${{env.BUILD_DIR}} && zip ${{env.ZIP_FILE}} ./
- name: Upload artifact
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v6
with:
name: app_${{env.BUILD_ARCH}}
path: ${{env.BUILD_DIR}}/${{env.ZIP_FILE}}
@@ -70,23 +70,22 @@ jobs:
env:
BUILD_ARCH: win-x86
BUILD_DIR: ./build/SQLCipherED.UI/Release/net6.0/win-x86
- BUILD_ARTIFACTS: SQLCipherED.dll runner-sqlcipher-ed.dll runner-sqlcipher-ed.exe runner-sqlcipher-ed.runtimeconfig.json
ZIP_FILE: sqlcipher-ed_win-x86.zip
steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-dotnet@v3
+ - uses: actions/checkout@v6
+ - uses: actions/setup-dotnet@v5
with:
dotnet-version: '6.x'
- name: Build CLI app
- run: dotnet build SQLCipherED.UI/SQLCipherED.UI.csproj -r ${{env.BUILD_ARCH}} -c Release
+ run: dotnet build SQLCipherED.UI/SQLCipherED.UI.csproj -r ${{env.BUILD_ARCH}} -c Release --self-contained
- name: Zip build
- run: cd ${{env.BUILD_DIR}} && zip ${{env.ZIP_FILE}} ${{env.BUILD_ARTIFACTS}}
+ run: cd ${{env.BUILD_DIR}} && zip ${{env.ZIP_FILE}} ./
- name: Upload artifact
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v6
with:
name: app_${{env.BUILD_ARCH}}
path: ${{env.BUILD_DIR}}/${{env.ZIP_FILE}}
@@ -98,37 +97,62 @@ jobs:
env:
BUILD_ARCH: linux-x64
BUILD_DIR: ./build/SQLCipherED.UI/Release/net6.0/linux-x64
- #BUILD_ARTIFACTS: SQLCipherED.so runner-sqlcipher-ed.so runner-sqlcipher-ed runner-sqlcipher-ed.runtimeconfig.json
- BUILD_ARTIFACTS: SQLCipherED.dll runner-sqlcipher-ed.dll runner-sqlcipher-ed runner-sqlcipher-ed.runtimeconfig.json
ZIP_FILE: sqlcipher-ed_linux-x64.zip
steps:
- - uses: actions/checkout@v3
- - uses: actions/setup-dotnet@v3
+ - uses: actions/checkout@v6
+ - uses: actions/setup-dotnet@v5
with:
dotnet-version: '6.x'
- name: Build CLI app
- run: dotnet build SQLCipherED.UI/SQLCipherED.UI.csproj -r ${{env.BUILD_ARCH}} -c Release
-
+ run: dotnet build SQLCipherED.UI/SQLCipherED.UI.csproj -r ${{env.BUILD_ARCH}} -c Release --self-contained
+
- name: Zip build
- run: cd ${{env.BUILD_DIR}} && zip ${{env.ZIP_FILE}} ${{env.BUILD_ARTIFACTS}}
+ run: cd ${{env.BUILD_DIR}} && zip ${{env.ZIP_FILE}} ./
- name: Upload artifact
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v6
with:
name: app_${{env.BUILD_ARCH}}
path: ${{env.BUILD_DIR}}/${{env.ZIP_FILE}}
+
+ build_nuget:
+ name: Build NuGet package
+ needs: unit_tests
+ runs-on: ubuntu-latest
+ env:
+ RELEASE_VERSION: ${{ needs.unit_tests.outputs.release_ver }}
+ BUILD_DIR: ./build/SQLCipherED/Release
+
+ steps:
+ - uses: actions/checkout@v6
+ - uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: |
+ 6.0.x
+ 8.0.x
+
+ - name: Release build
+ run: dotnet build SQLCipherED/SQLCipherED.csproj -c Release
+
+ - name: Upload artifact
+ uses: actions/upload-artifact@v6
+ with:
+ name: nuget_artifact
+ path: ${{ env.BUILD_DIR }}/SQLCipherED.${{ env.RELEASE_VERSION }}.nupkg
github_release_draft:
name: Make Github release draft
needs:
+ - unit_tests
- build_win_x64
- build_win_x86
- build_linux_x64
+ - build_nuget
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_BUILD_TOKEN }}
- RELEASE_VERSION: ${{ needs.build_win_x64.outputs.release_ver }}
+ RELEASE_VERSION: ${{ needs.unit_tests.outputs.release_ver }}
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
runs-on: ubuntu-latest
@@ -136,28 +160,25 @@ jobs:
steps:
- name: Create Release
id: create_release
- uses: actions/create-release@v1
+ uses: softprops/action-gh-release@v2
with:
tag_name: ${{ env.RELEASE_VERSION }}
- release_name: ${{ github.event.repository.name }} ${{ env.RELEASE_VERSION }}
+ name: ${{ github.event.repository.name }} ${{ env.RELEASE_VERSION }}
draft: true
- github_upload_release_artifacts:
- name: Upload build artifacts to GIthub release
+ github_upload_win_x64:
+ name: Upload Windows x64 build artifact to Github release
needs:
+ - unit_tests
- github_release_draft
- - build_win_x64
- - build_win_x86
- - build_linux_x64
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_BUILD_TOKEN }}
- RELEASE_VERSION: ${{ needs.build_win_x64.outputs.release_ver }}
UPLOAD_URL: ${{ needs.github_release_draft.outputs.upload_url }}
runs-on: ubuntu-latest
steps:
- name: Download win-x64 artifact
- uses: actions/download-artifact@v2
+ uses: actions/download-artifact@v4
with:
name: app_win-x64
@@ -166,11 +187,22 @@ jobs:
with:
upload_url: ${{ env.UPLOAD_URL }}
asset_path: sqlcipher-ed_win-x64.zip
- asset_name: ${{ github.event.repository.name }} ${{ env.RELEASE_VERSION }}_win-x64.zip
+ asset_name: ${{ github.event.repository.name }}_win-x64.zip
asset_content_type: application/zip
-
+
+ github_upload_win_x86:
+ name: Upload Windows x86 build artifact to Github release
+ needs:
+ - unit_tests
+ - github_release_draft
+ env:
+ GITHUB_TOKEN: ${{ secrets.RELEASE_BUILD_TOKEN }}
+ UPLOAD_URL: ${{ needs.github_release_draft.outputs.upload_url }}
+ runs-on: ubuntu-latest
+
+ steps:
- name: Download win-x86 artifact
- uses: actions/download-artifact@v2
+ uses: actions/download-artifact@v4
with:
name: app_win-x86
@@ -179,11 +211,23 @@ jobs:
with:
upload_url: ${{ env.UPLOAD_URL }}
asset_path: sqlcipher-ed_win-x86.zip
- asset_name: ${{ github.event.repository.name }} ${{ env.RELEASE_VERSION }}_win-x86.zip
+ asset_name: ${{ github.event.repository.name }}_win-x86.zip
asset_content_type: application/zip
-
+
+ github_upload_linux_x64:
+ name: Upload Linux x64 build artifacts (self-contained, AppImage) to Github release
+ needs:
+ - unit_tests
+ - github_release_draft
+ env:
+ GITHUB_TOKEN: ${{ secrets.RELEASE_BUILD_TOKEN }}
+ RELEASE_VERSION: ${{ needs.unit_tests.outputs.release_ver }}
+ UPLOAD_URL: ${{ needs.github_release_draft.outputs.upload_url }}
+ runs-on: ubuntu-latest
+
+ steps:
- name: Download linux-x64 artifact
- uses: actions/download-artifact@v2
+ uses: actions/download-artifact@v4
with:
name: app_linux-x64
@@ -193,4 +237,29 @@ jobs:
upload_url: ${{ env.UPLOAD_URL }}
asset_path: sqlcipher-ed_linux-x64.zip
asset_name: ${{ github.event.repository.name }} ${{ env.RELEASE_VERSION }}_linux-x64.zip
+ asset_content_type: application/zip
+
+ github_upload_nuget:
+ name: Upload NuGet build artifact to Github release
+ needs:
+ - unit_tests
+ - github_release_draft
+ env:
+ GITHUB_TOKEN: ${{ secrets.RELEASE_BUILD_TOKEN }}
+ RELEASE_VERSION: ${{ needs.unit_tests.outputs.release_ver }}
+ UPLOAD_URL: ${{ needs.github_release_draft.outputs.upload_url }}
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Download NuGet artifact
+ uses: actions/download-artifact@v7
+ with:
+ name: nuget_artifact
+
+ - name: Upload NuGet artifact to release assets
+ uses: actions/upload-release-asset@v1.0.1
+ with:
+ upload_url: ${{ env.UPLOAD_URL }}
+ asset_path: SQLCipherED.${{ env.RELEASE_VERSION }}.nupkg
+ asset_name: SQLCipherED.${{ env.RELEASE_VERSION }}.nupkg
asset_content_type: application/zip
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index c6aa90d..d184441 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2023 SQLCipherED
+Copyright (c) 2023-2026 mIwr
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/SQLCipherED.Tests/Directory.Build.props b/SQLCipherED.Tests/Directory.Build.props
new file mode 100644
index 0000000..475b477
--- /dev/null
+++ b/SQLCipherED.Tests/Directory.Build.props
@@ -0,0 +1,5 @@
+
+
+ ../build/$(MSBuildProjectName)/obj/
+
+
\ No newline at end of file
diff --git a/SQLCipherED.Tests/SQLCipherED.Tests.csproj b/SQLCipherED.Tests/SQLCipherED.Tests.csproj
index 4c65ab8..f82124a 100644
--- a/SQLCipherED.Tests/SQLCipherED.Tests.csproj
+++ b/SQLCipherED.Tests/SQLCipherED.Tests.csproj
@@ -11,23 +11,7 @@
../build/SQLCipherED.Tests
- x64;x86
-
-
-
- 7
-
-
-
- 7
-
-
-
- 7
-
-
-
- 7
+ AnyCPU
diff --git a/SQLCipherED.UI/Directory.Build.props b/SQLCipherED.UI/Directory.Build.props
new file mode 100644
index 0000000..475b477
--- /dev/null
+++ b/SQLCipherED.UI/Directory.Build.props
@@ -0,0 +1,5 @@
+
+
+ ../build/$(MSBuildProjectName)/obj/
+
+
\ No newline at end of file
diff --git a/SQLCipherED.UI/Program.cs b/SQLCipherED.UI/Program.cs
index 6984cfc..04d86cf 100644
--- a/SQLCipherED.UI/Program.cs
+++ b/SQLCipherED.UI/Program.cs
@@ -31,34 +31,37 @@ class Program
static void Main(string[] args)
{
- var parseStatus = ProcessArgs(args);
- if (parseStatus)
- {
- var sqlcipherCryptEng = new SQLCipherCryptEng(_kdfIter, _kdfAlgo, _pageSize, _kdfAlgo);
- var reader = new BinaryReader(File.OpenRead(_sourcePath));
- var outData = Array.Empty();
- Console.WriteLine("Decrypting '" + _sourcePath + '\'');
- try
+ if (!ProcessArgs(args))
+ {
+#if DEBUG
+ Console.ReadKey();
+#endif
+ return;
+ }
+ var sqlcipherCryptEng = new SQLCipherCryptEng(_kdfIter, _kdfAlgo, _pageSize, _kdfAlgo);
+ var reader = new BinaryReader(File.OpenRead(_sourcePath));
+ var outData = Array.Empty();
+ Console.WriteLine("Decrypting '" + _sourcePath + '\'');
+ try
+ {
+ if (_hexKey.Length != 0)
{
- if (_hexKey.Length != 0)
- {
- outData = sqlcipherCryptEng.Decode(reader, _hexKey);
- }
- else
- {
- outData = sqlcipherCryptEng.Decode(reader, _passphrase);
- }
- Console.WriteLine("Done");
+ outData = sqlcipherCryptEng.Decode(reader, _hexKey);
}
- catch (Exception ex)
- {
- Console.WriteLine("Error: " + ex.Message);
- }
- reader.Close();
- if (outData.Length != 0)
+ else
{
- File.WriteAllBytes(_outPath, outData);
- }
+ outData = sqlcipherCryptEng.Decode(reader, _passphrase);
+ }
+ Console.WriteLine("Done");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("Error: " + ex.Message);
+ }
+ reader.Close();
+ if (outData.Length != 0)
+ {
+ File.WriteAllBytes(_outPath, outData);
}
#if DEBUG
Console.ReadKey();
diff --git a/SQLCipherED.UI/SQLCipherED.UI.csproj b/SQLCipherED.UI/SQLCipherED.UI.csproj
index 102ce1a..e994288 100644
--- a/SQLCipherED.UI/SQLCipherED.UI.csproj
+++ b/SQLCipherED.UI/SQLCipherED.UI.csproj
@@ -5,31 +5,15 @@
net6.0
disable
enable
- x64;x86
+ AnyCPU
SQLCipherED.UI.Program
runner-sqlcipher-ed
../build/SQLCipherED.UI
- 0.3.1.3
- 0.3.1.3
+ 1.0.0
+ 1.0.0
False
-
- 7
-
-
-
- 7
-
-
-
- 7
-
-
-
- 7
-
-
diff --git a/SQLCipherED.sln b/SQLCipherED.sln
index 2d9188e..aa332da 100644
--- a/SQLCipherED.sln
+++ b/SQLCipherED.sln
@@ -11,36 +11,22 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SQLCipherED.Tests", "SQLCip
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|x64 = Debug|x64
- Debug|x86 = Debug|x86
- Release|x64 = Release|x64
- Release|x86 = Release|x86
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {F3E5C644-DD0C-4002-87AE-631273CE9DFD}.Debug|x64.ActiveCfg = Debug|x64
- {F3E5C644-DD0C-4002-87AE-631273CE9DFD}.Debug|x64.Build.0 = Debug|x64
- {F3E5C644-DD0C-4002-87AE-631273CE9DFD}.Debug|x86.ActiveCfg = Debug|x86
- {F3E5C644-DD0C-4002-87AE-631273CE9DFD}.Debug|x86.Build.0 = Debug|x86
- {F3E5C644-DD0C-4002-87AE-631273CE9DFD}.Release|x64.ActiveCfg = Release|x64
- {F3E5C644-DD0C-4002-87AE-631273CE9DFD}.Release|x64.Build.0 = Release|x64
- {F3E5C644-DD0C-4002-87AE-631273CE9DFD}.Release|x86.ActiveCfg = Release|x86
- {F3E5C644-DD0C-4002-87AE-631273CE9DFD}.Release|x86.Build.0 = Release|x86
- {EF1B7284-3B04-48F4-865D-71E2112F5DEB}.Debug|x64.ActiveCfg = Debug|x64
- {EF1B7284-3B04-48F4-865D-71E2112F5DEB}.Debug|x64.Build.0 = Debug|x64
- {EF1B7284-3B04-48F4-865D-71E2112F5DEB}.Debug|x86.ActiveCfg = Debug|x86
- {EF1B7284-3B04-48F4-865D-71E2112F5DEB}.Debug|x86.Build.0 = Debug|x86
- {EF1B7284-3B04-48F4-865D-71E2112F5DEB}.Release|x64.ActiveCfg = Release|x64
- {EF1B7284-3B04-48F4-865D-71E2112F5DEB}.Release|x64.Build.0 = Release|x64
- {EF1B7284-3B04-48F4-865D-71E2112F5DEB}.Release|x86.ActiveCfg = Release|x86
- {EF1B7284-3B04-48F4-865D-71E2112F5DEB}.Release|x86.Build.0 = Release|x86
- {8633AC27-412E-4DF9-B482-DF6891BC09AA}.Debug|x64.ActiveCfg = Debug|x64
- {8633AC27-412E-4DF9-B482-DF6891BC09AA}.Debug|x64.Build.0 = Debug|x64
- {8633AC27-412E-4DF9-B482-DF6891BC09AA}.Debug|x86.ActiveCfg = Debug|x86
- {8633AC27-412E-4DF9-B482-DF6891BC09AA}.Debug|x86.Build.0 = Debug|x86
- {8633AC27-412E-4DF9-B482-DF6891BC09AA}.Release|x64.ActiveCfg = Release|x64
- {8633AC27-412E-4DF9-B482-DF6891BC09AA}.Release|x64.Build.0 = Release|x64
- {8633AC27-412E-4DF9-B482-DF6891BC09AA}.Release|x86.ActiveCfg = Release|x86
- {8633AC27-412E-4DF9-B482-DF6891BC09AA}.Release|x86.Build.0 = Release|x86
+ {F3E5C644-DD0C-4002-87AE-631273CE9DFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F3E5C644-DD0C-4002-87AE-631273CE9DFD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F3E5C644-DD0C-4002-87AE-631273CE9DFD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F3E5C644-DD0C-4002-87AE-631273CE9DFD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EF1B7284-3B04-48F4-865D-71E2112F5DEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EF1B7284-3B04-48F4-865D-71E2112F5DEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EF1B7284-3B04-48F4-865D-71E2112F5DEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EF1B7284-3B04-48F4-865D-71E2112F5DEB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8633AC27-412E-4DF9-B482-DF6891BC09AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8633AC27-412E-4DF9-B482-DF6891BC09AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8633AC27-412E-4DF9-B482-DF6891BC09AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8633AC27-412E-4DF9-B482-DF6891BC09AA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/SQLCipherED/Constants.cs b/SQLCipherED/Constants.cs
index 0b6a002..1c5fcbb 100644
--- a/SQLCipherED/Constants.cs
+++ b/SQLCipherED/Constants.cs
@@ -1,10 +1,25 @@
namespace SQLCipherED
{
+ ///
+ /// SQLCipher constants
+ ///
internal static class Constants
- {
+ {
+ ///
+ /// Plain-text SQLite DB magic header
+ ///
internal const string SQLiteHeader = "SQLite format 3\0";
+ ///
+ /// SQLCipher salt size in bytes
+ ///
internal const byte SaltSize = 16;
+ ///
+ /// SQLCipher AES IV size in bytes
+ ///
internal const byte IVSize = 16;
+ ///
+ /// SQLCipher decrptyion key size in bytes
+ ///
internal const byte KeySize = 32;
}
}
diff --git a/SQLCipherED/Directory.Build.props b/SQLCipherED/Directory.Build.props
new file mode 100644
index 0000000..475b477
--- /dev/null
+++ b/SQLCipherED/Directory.Build.props
@@ -0,0 +1,5 @@
+
+
+ ../build/$(MSBuildProjectName)/obj/
+
+
\ No newline at end of file
diff --git a/SQLCipherED/SQLCipherCryptEng.cs b/SQLCipherED/SQLCipherCryptEng.cs
index 07cde8f..1568f1b 100644
--- a/SQLCipherED/SQLCipherCryptEng.cs
+++ b/SQLCipherED/SQLCipherCryptEng.cs
@@ -84,7 +84,14 @@ public SQLCipherCryptEng(SQLCipherStandard standard)
KdfIter = standard.KdfIter();
PbkdfAlgo = standard.PbkdfAlgo();
PageSize = standard.PageSize();
- HmacPbkdfAlgo = standard == SQLCipherStandard.V1 ? null : standard.PbkdfAlgo();
+ if (standard == SQLCipherStandard.V1)
+ {
+ HmacPbkdfAlgo = null;
+ }
+ else
+ {
+ HmacPbkdfAlgo = standard.PbkdfAlgo();
+ }
HmacKdfIter = standard.FastKdfIter();
HmacSaltMask = standard.HmacSaltMask();
}
@@ -95,6 +102,7 @@ public SQLCipherCryptEng(SQLCipherStandard standard)
/// Key derivation iterations count
/// Key derivation algo
/// SQLCipher db page size
+ /// .NET hash algo name
///
///
///
@@ -108,7 +116,7 @@ public SQLCipherCryptEng(int kdfIter, HashAlgorithmName kdfAlgo, ushort pageSize
{
throw new ArgumentException(message: "Incorrect HMAC KDF iterations value, must be at least 1");
}
- var logVal = Math.Log2(pageSize);
+ var logVal = Math.Log(pageSize, newBase: 2);
var delta = logVal - Math.Truncate(logVal);
if (pageSize < 512 || delta != 0.0)
{
@@ -329,7 +337,8 @@ public byte[] Decode(BinaryReader sqlcipher, byte[] keyBytes)
}
var decodedWriter = new MemoryStream();
- decodedWriter.Write(Encoding.UTF8.GetBytes(Constants.SQLiteHeader));
+ var bytes = Encoding.UTF8.GetBytes(Constants.SQLiteHeader);
+ decodedWriter.Write(bytes, offset: 0, count: bytes.Length);
var buffer = new byte[PageSize];
int readCount;
//Decrypt the custom 1st page
@@ -341,8 +350,8 @@ public byte[] Decode(BinaryReader sqlcipher, byte[] keyBytes)
if (finalBlock.Length != 0)
{
decodedWriter.Write(finalBlock, offset: 0, finalBlock.Length);
- }
- decodedWriter.Write(new byte[PageSize - readCount - Constants.SaltSize - finalBlock.Length]);
+ }
+ decodedWriter.Write(new byte[PageSize - readCount - Constants.SaltSize - finalBlock.Length], offset: 0, count: PageSize - readCount - Constants.SaltSize - finalBlock.Length);
//Decrypt the next pages
while (sqlcipher.BaseStream.Position < sqlcipher.BaseStream.Length)
@@ -373,7 +382,7 @@ public byte[] Decode(BinaryReader sqlcipher, byte[] keyBytes)
{
decodedWriter.Write(finalBlock, offset: 0, finalBlock.Length);
}
- decodedWriter.Write(new byte[PageSize - readCount - finalBlock.Length]);
+ decodedWriter.Write(new byte[PageSize - readCount - finalBlock.Length], offset: 0, count: PageSize - readCount - finalBlock.Length);
}
var decrypted = decodedWriter.ToArray();
decodedWriter.Close();
diff --git a/SQLCipherED/SQLCipherED.csproj b/SQLCipherED/SQLCipherED.csproj
index d85d9fc..9eb6f6d 100644
--- a/SQLCipherED/SQLCipherED.csproj
+++ b/SQLCipherED/SQLCipherED.csproj
@@ -1,15 +1,14 @@
- net6.0
+ netstandard2.0;netstandard2.1;net6.0;net8.0
disable
- enable
- x64;x86
+ AnyCPU
../build/SQLCipherED
- False
+ True
SQLCipher DB Decrypt
- 0.3.1.3
- 0.3.1.3
+ 0.3.3
+ 0.3.3
LICENSE
True
$(AssemblyVersion)
@@ -17,25 +16,12 @@
https://github.com/mIwr/SQLCipherED
README.md
https://github.com/mIwr/SQLCipherED
- Github
+
6.0
False
-
-
-
- 7
-
-
-
- 7
-
-
-
- 7
-
-
-
- 7
+ True
+ mIwr
+ AnyCPU
diff --git a/SQLCipherED/SQLCipherHashAlgo.cs b/SQLCipherED/SQLCipherHashAlgo.cs
index 3fcdb97..bf95c8c 100644
--- a/SQLCipherED/SQLCipherHashAlgo.cs
+++ b/SQLCipherED/SQLCipherHashAlgo.cs
@@ -2,13 +2,35 @@
namespace SQLCipherED
{
+ ///
+ /// SQLCipher hash algo
+ ///
public enum SQLCipherHashAlgo: byte
{
- SHA1, SHA256, SHA512
+ ///
+ /// SHA1
+ ///
+ SHA1,
+ ///
+ /// SHA256
+ ///
+ SHA256,
+ ///
+ /// SHA512
+ ///
+ SHA512
}
+ ///
+ /// SQLCipher hash algo extension
+ ///
public static class SQLCipherHashAlgoExt
{
+ ///
+ /// Tries to parse SQLCipher hash algo from .NET hash algo
+ ///
+ /// .NET hash algo name
+ ///
public static SQLCipherHashAlgo? From(HashAlgorithmName algName)
{
var name = algName.Name ?? string.Empty;
@@ -28,6 +50,11 @@ public static class SQLCipherHashAlgoExt
return null;
}
+ ///
+ /// .NET hash algo
+ ///
+ /// SQLCipher hash algo
+ ///
public static HashAlgorithmName HashAlg(this SQLCipherHashAlgo algo)
{
switch (algo)
@@ -40,6 +67,11 @@ public static HashAlgorithmName HashAlg(this SQLCipherHashAlgo algo)
return HashAlgorithmName.SHA1;
}
+ ///
+ /// Hash size
+ ///
+ /// SQLCipher hash algo
+ ///
public static byte Size(this SQLCipherHashAlgo algo)
{
switch (algo)
diff --git a/SQLCipherED/SQLCipherStandard.cs b/SQLCipherED/SQLCipherStandard.cs
index a311cff..a4aedd6 100644
--- a/SQLCipherED/SQLCipherStandard.cs
+++ b/SQLCipherED/SQLCipherStandard.cs
@@ -9,9 +9,27 @@ namespace SQLCipherED
///
public enum SQLCipherStandard : byte
{
- V1 = 1, V2 = 2, V3 = 3, V4 = 4
+ ///
+ /// Revision 1 - no tag (0 bytes)
+ ///
+ V1 = 1,
+ ///
+ /// Revision 2 - SHA1 tag (20 bytes)
+ ///
+ V2 = 2,
+ ///
+ /// Revision 3 - SHA1 tag (20 bytes)
+ ///
+ V3 = 3,
+ ///
+ /// Revision 4 - SHA512 (64 bytes) or optionally SHA256 (32 bytes) tag
+ ///
+ V4 = 4
}
+ ///
+ /// Default SQLCipher settings extension
+ ///
public static class SQLCipherStandardExt
{
internal const byte SHA1Size = 20;
@@ -28,22 +46,30 @@ public static class SQLCipherStandardExt
internal const byte FastPbkdf2Iter = 2;
internal const byte DefaultHmacSaltMask = 0x3a;
+ ///
+ /// Tries to parse SQLCipher revision standard from number
+ ///
+ /// Version number
+ /// SQLCipher revision standard. Optional
public static SQLCipherStandard? From(byte versionNumber)
- {
- var values = Enum.GetValues();
- foreach (var enumItem in values)
+ {
+ switch(versionNumber)
{
- if ((byte)enumItem != versionNumber)
- {
- continue;
- }
- return enumItem;
+ case 1: return SQLCipherStandard.V1;
+ case 2: return SQLCipherStandard.V2;
+ case 3: return SQLCipherStandard.V3;
+ case 4: return SQLCipherStandard.V4;
}
return null;
}
- public static int KdfIter (this SQLCipherStandard standard)
+ ///
+ /// Default key derivation iterations count
+ ///
+ /// SQLCipher revision standard
+ ///
+ public static int KdfIter(this SQLCipherStandard standard)
{
switch(standard)
{
@@ -53,11 +79,21 @@ public static int KdfIter (this SQLCipherStandard standard)
}
}
+ ///
+ /// Default fast key derivation iterations count
+ ///
+ /// SQLCipher revision standard
+ ///
public static int FastKdfIter(this SQLCipherStandard standard)
{
return FastPbkdf2Iter;
}
+ ///
+ /// Default key derivation .NET hash algo
+ ///
+ /// SQLCipher revision standard
+ ///
public static HashAlgorithmName PbkdfAlgoName(this SQLCipherStandard standard)
{
switch (standard)
@@ -67,6 +103,11 @@ public static HashAlgorithmName PbkdfAlgoName(this SQLCipherStandard standard)
}
}
+ ///
+ /// Default key derivation SQLCipher hash algo
+ ///
+ /// SQLCipher revision standard
+ ///
public static SQLCipherHashAlgo PbkdfAlgo(this SQLCipherStandard standard)
{
switch(standard)
@@ -76,6 +117,11 @@ public static SQLCipherHashAlgo PbkdfAlgo(this SQLCipherStandard standard)
}
}
+ ///
+ /// Default SQLCipher data page size in bytes
+ ///
+ /// SQLCipher revision standard
+ ///
public static ushort PageSize(this SQLCipherStandard standard)
{
switch(standard)
@@ -85,6 +131,11 @@ public static ushort PageSize(this SQLCipherStandard standard)
}
}
+ ///
+ /// SQLCipher HMAC salt mask magic byte
+ ///
+ /// SQLCipher revision standard
+ ///
public static byte HmacSaltMask(this SQLCipherStandard standard)
{
return DefaultHmacSaltMask;
diff --git a/SQLCipherED/SQLCipherUtil.cs b/SQLCipherED/SQLCipherUtil.cs
index 846c843..38cc12a 100644
--- a/SQLCipherED/SQLCipherUtil.cs
+++ b/SQLCipherED/SQLCipherUtil.cs
@@ -4,8 +4,16 @@
namespace SQLCipherED
{
+ ///
+ /// SQLCipher utils
+ ///
public static class SQLCipherUtil
{
+ ///
+ /// Checks the database bytes (encrypted/decrypted)
+ ///
+ /// Raw DB bytes
+ /// Returns False, if the database in hex starts from magic header, otherwise - True
public static bool IsEncrypted(byte[] sqlcipherBytes)
{
if (sqlcipherBytes.Length < Constants.SQLiteHeader.Length)
@@ -26,31 +34,77 @@ public static bool IsEncrypted(byte[] sqlcipherBytes)
return false;
}
+ ///
+ /// Checks the data base bytes (encrypted/decrypted)
+ ///
+ /// Raw DB bytes
+ /// >Returns True, if the database in hex starts from magic header, otherwise - False
public static bool IsDecrypted(byte[] sqlcipherBytes)
{
return !IsEncrypted(sqlcipherBytes);
}
+ ///
+ /// Generates randomized salt for SQLCipher encryption/decryption ops
+ ///
+ /// Salt bytes
public static byte[] GenerateSalt()
{
- var salt = RandomNumberGenerator.GetBytes(Constants.SaltSize);
+ var rnd = RandomNumberGenerator.Create();
+ var salt = new byte[Constants.SaltSize];
+ rnd.GetBytes(salt);
return salt;
}
+ ///
+ /// Generates SQLCipher encryption/decryption key
+ ///
+ /// Salt
+ /// Passphrase bytes
+ /// Result key derrivation iterations count
+ /// Result key derrivation hash algo
+ /// Result key size
+ ///
public static byte[] GenerateKey(byte[] salt, byte[] passBytes, int kdfIter, HashAlgorithmName hashAlgo, byte keySize)
{
+#if NET
var key = Rfc2898DeriveBytes.Pbkdf2(passBytes, salt, kdfIter, hashAlgo, keySize);
-
+#else
+ var key = PBKDF2.DeriveBytes(hashAlgo.Name, salt, passBytes, kdfIter, keySize);
+#endif
return key;
}
+ ///
+ /// Generates SQLCipher encryption/decryption key
+ ///
+ /// Salt
+ /// Passhrase string
+ /// Result key derrivation iterations count
+ /// Result key derrivation hash algo
+ /// Result key size
+ ///
public static byte[] GenerateKey(byte[] salt, string passphrase, int kdfIter, HashAlgorithmName hashAlgo, byte keySize)
{
+#if NET
var key = Rfc2898DeriveBytes.Pbkdf2(passphrase, salt, kdfIter, hashAlgo, keySize);
-
+#else
+ var passBytes = Encoding.UTF8.GetBytes(passphrase);
+ var key = PBKDF2.DeriveBytes(hashAlgo.Name, salt, passBytes, kdfIter, keySize);
+#endif
return key;
}
+ ///
+ /// Generates SQLCipher data page HMAC
+ ///
+ /// HMAC key
+ /// Page data raw bytes
+ /// Page index
+ /// IV + HMAC (if exists) bytes count at the end of page
+ /// SQLCipher HMAC algo
+ /// Returns generated HMAC of the page data
+ /// Throws, when unable to convert SQLCipher hash algo to .NET hash algo
public static byte[] GeneratePageHMAC(byte[] hmacKey, byte[] pageBytes, int sqlCipherPageIndex, int reserveSize, SQLCipherHashAlgo hmacAlgo)
{
HMAC hmac;
@@ -79,6 +133,15 @@ public static byte[] GeneratePageHMAC(byte[] hmacKey, byte[] pageBytes, int sqlC
return calculatedHmac;
}
+ ///
+ /// Compares generated and stock checksums of the page
+ ///
+ /// HMAC key
+ /// Raw page bytes
+ /// Page index
+ /// IV + HMAC (if exists) bytes count at the end of page
+ /// SQLCipher HMAC algo
+ /// Returns True, if generated and stock checksums are equal, which means correct decryption. Otherwise returns False
public static bool CheckPageHMAC(byte[] hmacKey, byte[] pageBytes, int sqlCipherPageIndex, int reserveSize, SQLCipherHashAlgo hmacAlgo)
{
var pageHmac = new byte[hmacAlgo.Size()];
@@ -102,4 +165,171 @@ public static bool CheckPageHMAC(byte[] hmacKey, byte[] pageBytes, int sqlCipher
return true;
}
}
+
+#if !NET
+ #region .NET Standard PBKDF2
+ ///
+ /// Provides an easy to understand PBKDF2 implementation.
+ /// Note: This will create correct values, but it's very slow.
+ /// In security critical applications you want to use instead.
+ ///
+ internal static class PBKDF2
+ {
+ ///
+ /// Derives bytes using PBKDF2
+ ///
+ ///
+ /// This is the name of the hash function. Usually SHA1
+ /// but can be any other such as (but not exhaustively):
+ /// SHA256, SHA512, RIPEMD160.
+ /// Any algorithm that has a matching HMAC function will work.
+ ///
+ ///
+ /// Salt for the function.
+ /// This should be randomly generated and be between 16 and 32 bytes.
+ /// You need to store this somewhere if you want to create the same hash later.
+ ///
+ ///
+ /// Key for the function.
+ /// If your key is a string, use Encoding.UTF8.GetBytes(string) to convert it into a byte array.
+ ///
+ ///
+ /// The number of iterations.
+ /// Normally you want this to be 100'000 or more,
+ /// but this implementation is not made to be fast, but easy to understand.
+ /// Keep it around 1'000 - 10'000 for this demo.
+ ///
+ ///
+ /// The number of bytes you want to get out of this function
+ ///
+ ///
+ /// Randomly looking but deterministic bytes
+ ///
+ internal static byte[] DeriveBytes(string rngFunction, byte[] salt, byte[] password, int iterations, int byteCount)
+ {
+ #region Validation
+ //Make sure something was specified
+ if (string.IsNullOrEmpty(rngFunction))
+ {
+ throw new ArgumentException($"'{nameof(rngFunction)}' cannot be null or empty.", nameof(rngFunction));
+ }
+ //Cannot use null as salt
+ if (salt is null)
+ {
+ throw new ArgumentNullException(nameof(salt));
+ }
+ //Cannot use null as password either
+ if (password is null)
+ {
+ throw new ArgumentNullException(nameof(password));
+ }
+ //Require at least one iteration
+ if (iterations < 1)
+ {
+ throw new ArgumentOutOfRangeException(nameof(iterations));
+ }
+ //Require at least one output byte
+ if (byteCount < 1)
+ {
+ throw new ArgumentOutOfRangeException(nameof(byteCount));
+ }
+ #endregion
+
+ //Create the HMAC of the supplied algorithm
+ var keyHash = HMAC.Create("HMAC" + rngFunction);
+ if (keyHash == null)
+ {
+ return Array.Empty();
+ }
+
+ //Size of the algorithm in bytes.
+ //Note that sizes in cryptographic algorithms are often reported in bits, not bytes,
+ //hence why we divide the value by 8
+ var size = keyHash.HashSize / 8;
+
+ using (keyHash)
+ {
+ //Number of hash blocks we need to fit the requested byte count
+ var BlockCount = byteCount / size;
+ //If there is a remainder, we need to add one extra block
+ //For example with SHA1 (block size 20 bytes) and 30 bytes of data:
+ //30/20=1 (integer division always rounds down)
+ //30%20=10 (remainder is not zero, so increase block size by one)
+ //Final size: 2 blocks
+ //If you prefer this in one line:
+ //BlockCount = ByteCount / Size + (ByteCount % Size == 0 ? 0 : 1);
+ if (byteCount % size != 0)
+ {
+ ++BlockCount;
+ }
+ //This holds the final output data
+ byte[] Output = new byte[byteCount];
+ //Set key
+ keyHash.Key = password;
+ //Hash as many blocks as needed
+ for (var BlockIndex = 1; BlockIndex <= BlockCount; BlockIndex++)
+ {
+ //Hash a block of data
+ var Round = HashBlock(keyHash, salt, BlockIndex, iterations);
+ //The location of where the data goes in the output
+ //This basically starts each output at the end of the previous one
+ var BlockOffset = (BlockIndex - 1) * size;
+ //Normally we copy all bytes but the last chunk may be smaller
+ var BytesToCopy = Math.Min(Round.Length, byteCount - BlockOffset);
+ Array.Copy(Round, 0, Output, BlockOffset, BytesToCopy);
+ }
+ return Output;
+ }
+ }
+
+ ///
+ /// Hashes a block of output data
+ ///
+ /// Hash algorithm
+ /// Salt value
+ /// Block index (starts at 1, not 0)
+ /// Number of iterations
+ /// Hashed block data
+ private static byte[] HashBlock(KeyedHashAlgorithm Hasher, byte[] Salt, int BlockIndex, int IterationCount)
+ {
+ //First round is special by additionally using the block index in the input
+ byte[] Data = Hasher.ComputeHash(GetFirstBlockData(Salt, BlockIndex));
+ //Holds the final result
+ byte[] Result = (byte[])Data.Clone();
+ //rounds 2 to IterationCount use the result "Data" of the previous run
+ for (var i = 2; i <= IterationCount; i++)
+ {
+ byte[] Temp = Hasher.ComputeHash(Data);
+ //The result is XOR combined with the input data
+ for (var j = 0; j < Temp.Length; j++)
+ {
+ Result[j] ^= Temp[j];
+ }
+ //Use the last result as the data for the next iteration
+ Data = Temp;
+ }
+ return Result;
+ }
+
+ ///
+ /// Gets the value used for the first block hash
+ ///
+ /// Salt
+ /// Block index (starts at 1, not 0)
+ /// value for first iteration of the hasher
+ private static byte[] GetFirstBlockData(byte[] Salt, int BlockIndex)
+ {
+ var Data = new byte[Salt.Length + 4];
+ //Copy key into the buffer
+ Array.Copy(Salt, Data, Salt.Length);
+ //Append the BlockIndex as 32 bit big endian integer
+ Data[Salt.Length] = (byte)(BlockIndex >> 24);
+ Data[Salt.Length + 1] = (byte)(BlockIndex >> 16 & 0xFF);
+ Data[Salt.Length + 2] = (byte)(BlockIndex >> 8 & 0xFF);
+ Data[Salt.Length + 3] = (byte)(BlockIndex & 0xFF);
+ return Data;
+ }
+ }
+ #endregion
+#endif
}
\ No newline at end of file