Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
2554fc8e41 | |||
aeaa8933cd | |||
b258cf41d0 | |||
6f9d4c6aa6 | |||
10e0c7446b | |||
395d5628d8 | |||
d19ed2d57f | |||
f458c053c8 | |||
f364acaec3 | |||
929acfab04 | |||
535795901d | |||
b5a1ea6089 | |||
567fb01e79 | |||
2fd8f75279 |
0
.gitattributes
vendored
Normal file
0
.gitattributes
vendored
Normal file
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
build/
|
build/
|
||||||
.cache/
|
.cache/
|
||||||
cmake-build-*
|
cmake-build-*
|
||||||
|
.vscode/
|
||||||
|
@ -1,6 +1,26 @@
|
|||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.14)
|
||||||
|
|
||||||
project(kgg-dec VERSION 0.5.1 LANGUAGES CXX)
|
project(kgg-dec VERSION 0.6.1 LANGUAGES CXX)
|
||||||
|
|
||||||
|
option(USE_SYSTEM_SQLITE3 "Use system SQLite3 (if not using WinSQLite3)" ON)
|
||||||
|
option(USE_OPENSSL "Use OpenSSL API (if not using WinCrypto API)" ON)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
option(USE_WIN_SQLITE3 "Use Windows SQLite3 (MSVC Only)" ${MSVC})
|
||||||
|
option(USE_WIN_CRYPTO "Use Windows Crypto API" ${WIN32})
|
||||||
|
else()
|
||||||
|
set(USE_WIN_SQLITE3 OFF)
|
||||||
|
set(USE_WIN_CRYPTO OFF)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Setup CryptoAPI
|
||||||
|
if (NOT USE_WIN_CRYPTO AND USE_OPENSSL)
|
||||||
|
find_package(OpenSSL REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(cmake/SetupSQLite3.cmake)
|
||||||
|
add_subdirectory(third-party/aes)
|
||||||
|
add_subdirectory(third-party/md5)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
@ -23,6 +43,29 @@ target_include_directories(kgg-dec
|
|||||||
src/tc_tea
|
src/tc_tea
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(kgg-dec PRIVATE shell32 ole32)
|
# Base crypto implementations
|
||||||
target_compile_definitions(kgg-dec PRIVATE NOMINMAX)
|
target_link_libraries(kgg-dec PRIVATE libaes libmd5)
|
||||||
|
if (USE_WIN_CRYPTO)
|
||||||
|
target_compile_definitions(kgg-dec PRIVATE USE_WIN_CRYPTO=1)
|
||||||
|
elseif(USE_OPENSSL)
|
||||||
|
target_compile_definitions(kgg-dec PRIVATE USE_OPENSSL=1)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Win32 specific
|
||||||
|
if (WIN32)
|
||||||
|
target_link_libraries(kgg-dec PRIVATE shell32 ole32)
|
||||||
|
target_compile_definitions(kgg-dec PRIVATE NOMINMAX)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# SQLite3
|
||||||
|
if (WinSQLite3_Found)
|
||||||
|
target_link_libraries(kgg-dec PRIVATE WinSQLite3)
|
||||||
|
target_include_directories(kgg-dec PRIVATE ${WindowsKitInclude})
|
||||||
|
elseif(SQLite3_FOUND)
|
||||||
|
target_link_libraries(kgg-dec PRIVATE SQLite::SQLite3)
|
||||||
|
else ()
|
||||||
|
target_link_libraries(kgg-dec PRIVATE sqlite3)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Extra definitions
|
||||||
target_compile_definitions(kgg-dec PRIVATE KGGDEC_PROJECT_VERSION="${PROJECT_VERSION}")
|
target_compile_definitions(kgg-dec PRIVATE KGGDEC_PROJECT_VERSION="${PROJECT_VERSION}")
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
{
|
{
|
||||||
"name": "default",
|
"name": "default",
|
||||||
"hidden": true,
|
"hidden": true,
|
||||||
"generator": "Ninja",
|
"generator": "Ninja Multi-Config",
|
||||||
"cacheVariables": {
|
"cacheVariables": {
|
||||||
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
||||||
}
|
}
|
||||||
@ -29,13 +29,22 @@
|
|||||||
"CMAKE_EXE_LINKER_FLAGS": "-static -static-libgcc -static-libstdc++ -lucrt"
|
"CMAKE_EXE_LINKER_FLAGS": "-static -static-libgcc -static-libstdc++ -lucrt"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "ninja",
|
||||||
|
"inherits": "default",
|
||||||
|
"description": "Default Ninja Configuration",
|
||||||
|
"binaryDir": "${sourceDir}/build/default"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "vs",
|
"name": "vs",
|
||||||
"inherits": "default",
|
"inherits": "default",
|
||||||
"description": "Configure for Visual Studio",
|
"description": "Configure for Visual Studio",
|
||||||
"generator": "Visual Studio 17 2022",
|
"generator": "Visual Studio 17 2022",
|
||||||
"binaryDir": "${sourceDir}/build/vs2022",
|
"binaryDir": "${sourceDir}/build/vs2022",
|
||||||
"architecture": "Win32"
|
"architecture": "x64,version=10.0",
|
||||||
|
"toolset": {
|
||||||
|
"value": "v143"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"buildPresets": [
|
"buildPresets": [
|
||||||
@ -51,6 +60,18 @@
|
|||||||
"description": "Build using MinGW (Release)",
|
"description": "Build using MinGW (Release)",
|
||||||
"configuration": "Release"
|
"configuration": "Release"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "ninja-debug",
|
||||||
|
"configurePreset": "ninja",
|
||||||
|
"description": "Build using Ninja (Debug)",
|
||||||
|
"configuration": "Debug"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ninja-release",
|
||||||
|
"configurePreset": "ninja",
|
||||||
|
"description": "Build using Ninja (Release)",
|
||||||
|
"configuration": "Release"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "vs-debug",
|
"name": "vs-debug",
|
||||||
"configurePreset": "vs",
|
"configurePreset": "vs",
|
||||||
|
8
Jenkinsfile
vendored
8
Jenkinsfile
vendored
@ -27,6 +27,14 @@ pipeline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stage('Prepare') {
|
||||||
|
steps {
|
||||||
|
dir('third-party/sqlite3') {
|
||||||
|
sh './fetch_sqlite3.sh'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
stage('Build') {
|
stage('Build') {
|
||||||
steps {
|
steps {
|
||||||
bat '''
|
bat '''
|
||||||
|
57
README.MD
57
README.MD
@ -2,21 +2,20 @@
|
|||||||
|
|
||||||
酷狗 `kgg` 文件解密工具。
|
酷狗 `kgg` 文件解密工具。
|
||||||
|
|
||||||
|
请尽量在下载文件的设备上操作,避免密钥丢失。
|
||||||
|
|
||||||
## 使用方法 (快捷)
|
## 使用方法 (快捷)
|
||||||
|
|
||||||
1. 将 `kgg-dec.exe` 与酷狗安装目录下的 `infra.dll` 拷贝到 `kgg` 文件所在目录。
|
1. 双击 `kgg-dec.exe` 开始解密当前目录。
|
||||||
2. 双击 `kgg-dec.exe` 开始解密当前目录。
|
|
||||||
|
|
||||||
## 使用方法 (命令行)
|
## 使用方法 (命令行)
|
||||||
|
|
||||||
1. 从酷狗安装目录拷贝 `infra.dll` 文件到 `kgg-dec.exe` 的目录。
|
1. 启动 `kgg-dec.exe`,其中第一个参数为含有 `kgg` 文件的目录。
|
||||||
2. 启动 `kgg-dec.exe`,其中第一个参数为含有 `kgg` 文件的目录。
|
2. 你也可以使用 `--` 来将参数后的 `-` 开头的参数视为输入文件或目录。
|
||||||
3. 你也可以使用 `--` 来将参数后的 `-` 开头的参数视为输入文件或目录。
|
3. 你可以指定多项输入文件或目录。
|
||||||
4. 你可以指定多项输入文件或目录。
|
|
||||||
|
|
||||||
### 其它参数
|
### 其它参数
|
||||||
|
|
||||||
* `--infra-dll` (可选): 指定 `infra.dll` 的路径,默认为 `infra.dll`。
|
|
||||||
* `--scan-all-file-ext` (可选,`0` 或 `1`): 是否扫描所有文件后缀名。默认为 `0`,只扫描 `kgg` 文件。
|
* `--scan-all-file-ext` (可选,`0` 或 `1`): 是否扫描所有文件后缀名。默认为 `0`,只扫描 `kgg` 文件。
|
||||||
* `--db` (可选): 指定 `KGMusicV3.db` 的路径。默认为 `%AppData%/Kugou8/KGMusicV3.db`。
|
* `--db` (可选): 指定 `KGMusicV3.db` 的路径。默认为 `%AppData%/Kugou8/KGMusicV3.db`。
|
||||||
* `--suffix` (可选): 指定解密后文件的后缀。默认为 `_kgg-dec`。
|
* `--suffix` (可选): 指定解密后文件的后缀。默认为 `_kgg-dec`。
|
||||||
@ -33,6 +32,7 @@
|
|||||||
## 错误排查
|
## 错误排查
|
||||||
|
|
||||||
1. 需要至少播放一次 `kgg` 文件,并确保酷狗能正常播放。
|
1. 需要至少播放一次 `kgg` 文件,并确保酷狗能正常播放。
|
||||||
|
- 如果还是无效,请尝试使用酷狗重新下载,在同一个设备上进行下载与解密操作。
|
||||||
2. 文件后缀名嗅探代码只支持:`ogg` / `flac`。
|
2. 文件后缀名嗅探代码只支持:`ogg` / `flac`。
|
||||||
* 其他格式会被识别为 `mp3`。
|
* 其他格式会被识别为 `mp3`。
|
||||||
|
|
||||||
@ -40,8 +40,51 @@
|
|||||||
|
|
||||||
在 Windows 下使用 CMake + Visual Studio 2022 构建。
|
在 Windows 下使用 CMake + Visual Studio 2022 构建。
|
||||||
|
|
||||||
|
第一次构建前请到 `third-party/sqlite3` 目录下执行 `fetch_sqlite3.sh`,或手动下载源码放置到对应的位置。
|
||||||
|
|
||||||
|
然后就可以利用 CMake 构建了:
|
||||||
|
|
||||||
```cmd
|
```cmd
|
||||||
cmake --preset vs -DCMAKE_BUILD_TYPE=Release
|
cmake --preset vs -DCMAKE_BUILD_TYPE=Release
|
||||||
cmake --build --preset vs-release --config Release
|
cmake --build --preset vs-release --config Release
|
||||||
copy /y README.MD .\\build\\vs2022\\
|
copy /y README.MD .\\build\\vs2022\\
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### CMake 参数
|
||||||
|
|
||||||
|
CMake 支持以下参数:
|
||||||
|
|
||||||
|
- `USE_WIN_SQLITE3` - 使用 Windows 内置的 SQLite3 链接库(仅限 Windows + MSVC 编译环境)。
|
||||||
|
- `USE_WIN_CRYPTO` - 使用 Windows 内置的加密/哈希实现,而非软件实现(仅限 Windows 目标)。
|
||||||
|
- `USE_SYSTEM_SQLITE3` - 使用外部的 SQLite3 实现 (Debian: `libsqlite3-dev`),找不到时会报错。
|
||||||
|
- `USE_OpenSSL` - 使用 OpenSSL 的加密/哈希实现 (Debian: `libssl-dev`),找不到时会报错。
|
||||||
|
|
||||||
|
Ubuntu 下编译发布版,不使用外部库:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cmake -G "Ninja Multi-Config" -B build/linux-all -DUSE_SYSTEM_SQLITE3=0 -DUSE_OPENSSL=0
|
||||||
|
cmake --build build/linux-all --config Release -j
|
||||||
|
```
|
||||||
|
|
||||||
|
Ubuntu 下编译发布版,链接到现有的库:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cmake -G "Ninja Multi-Config" -B build/linux-ext -DUSE_SYSTEM_SQLITE3=1 -DUSE_OPENSSL=1
|
||||||
|
cmake --build build/linux-ext --config Release -j
|
||||||
|
```
|
||||||
|
|
||||||
|
### 第三方软件
|
||||||
|
|
||||||
|
该程序用到了以下第三方软件:
|
||||||
|
|
||||||
|
- [SQLite3](https://www.sqlite.org/) (Public Domain)
|
||||||
|
- [Tiny AES in C](https://github.com/kokke/tiny-AES-c) (Public Domain)
|
||||||
|
- [MD5.c](https://github.com/freebsd/freebsd-src/blob/release/14.2.0/sys/kern/md5c.c) (from FreeBSD)
|
||||||
|
- Derived from the "RSA Data Security, Inc. MD5 Message-Digest Algorithm".
|
||||||
|
|
||||||
|
### Windows 7 用户注意
|
||||||
|
|
||||||
|
请从 SQLite 官网下载 [`sqlite-dll-win-x64-*.zip`](https://www.sqlite.org/download.html#win32)
|
||||||
|
并将压缩包内的 `sqlite3.dll` 放置到 `kgg-dec.exe` 同目录下,并更名为 `winsqlite3.dll`。
|
||||||
|
|
||||||
|
Windows 10 或更新版本不需要此操作,因为 Windows 10 或以上的版本内置该文件。
|
||||||
|
24
cmake/SetupSQLite3.cmake
Normal file
24
cmake/SetupSQLite3.cmake
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
set(WinSQLite3_Found FALSE)
|
||||||
|
|
||||||
|
if (MSVC AND USE_WIN_SQLITE3)
|
||||||
|
set(ProgramFiles_x86 "$ENV{ProgramFiles\(x86\)}")
|
||||||
|
file(GLOB WindowsKitLibs "${ProgramFiles_x86}/Windows Kits/10/Lib/10.0.*/um/${CMAKE_VS_PLATFORM_NAME}")
|
||||||
|
find_library(LibWinSQLite3 WinSQLite3 PATHS ${WindowsKitLibs})
|
||||||
|
if (LibWinSQLite3)
|
||||||
|
set(WinSQLite3_Found TRUE)
|
||||||
|
get_filename_component(WindowsKitVersion "${LibWinSQLite3}/../../.." ABSOLUTE)
|
||||||
|
get_filename_component(WindowsKitVersion ${WindowsKitVersion} NAME)
|
||||||
|
set(WindowsKitInclude "${ProgramFiles_x86}/Windows Kits/10/Include/${WindowsKitVersion}/um")
|
||||||
|
message("Using WinSQLite3 from Windows SDK (${WindowsKitVersion}).")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (NOT WinSQLite3_Found)
|
||||||
|
if (USE_SYSTEM_SQLITE3)
|
||||||
|
message("Using existing SQLite3.")
|
||||||
|
find_package(SQLite3 REQUIRED)
|
||||||
|
else()
|
||||||
|
message("including sqlite3 to the build")
|
||||||
|
add_subdirectory(third-party/sqlite3)
|
||||||
|
endif()
|
||||||
|
endif()
|
@ -1,7 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <bit>
|
#include <bit>
|
||||||
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
#define bswap_u16 _byteswap_ushort
|
#define bswap_u16 _byteswap_ushort
|
||||||
@ -15,35 +17,87 @@
|
|||||||
|
|
||||||
namespace Endian {
|
namespace Endian {
|
||||||
|
|
||||||
inline uint64_t be_u64_read(const uint8_t* p) {
|
template <typename T>
|
||||||
|
T be_read(const uint8_t* p)
|
||||||
|
requires(std::is_integral_v<T>)
|
||||||
|
{
|
||||||
|
// ReSharper disable once CppDFAUnreachableCode
|
||||||
if constexpr (std::endian::native == std::endian::big) {
|
if constexpr (std::endian::native == std::endian::big) {
|
||||||
return *reinterpret_cast<const uint64_t*>(p);
|
return *reinterpret_cast<const T*>(p);
|
||||||
|
} else if constexpr (sizeof(T) == 2) {
|
||||||
|
return bswap_u16(*reinterpret_cast<const T*>(p));
|
||||||
|
} else if constexpr (sizeof(T) == 4) {
|
||||||
|
return bswap_u32(*reinterpret_cast<const T*>(p));
|
||||||
|
} else if constexpr (sizeof(T) == 8) {
|
||||||
|
return bswap_u64(*reinterpret_cast<const T*>(p));
|
||||||
} else {
|
} else {
|
||||||
return bswap_u64(*reinterpret_cast<const uint64_t*>(p));
|
T result{};
|
||||||
|
for (size_t i = 0; i < sizeof(T); i++) {
|
||||||
|
reinterpret_cast<uint8_t*>(&result)[i] = p[sizeof(T) - i - 1];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void be_u64_write(uint8_t* p, uint64_t value) {
|
template <typename T>
|
||||||
|
void be_write(uint8_t* p, const T value)
|
||||||
|
requires(std::is_integral_v<T>)
|
||||||
|
{
|
||||||
|
// ReSharper disable once CppDFAUnreachableCode
|
||||||
if constexpr (std::endian::native == std::endian::big) {
|
if constexpr (std::endian::native == std::endian::big) {
|
||||||
*reinterpret_cast<uint64_t*>(p) = value;
|
*reinterpret_cast<T*>(p) = value;
|
||||||
|
} else if constexpr (sizeof(T) == 2) {
|
||||||
|
*reinterpret_cast<T*>(p) = bswap_u16(value);
|
||||||
|
} else if constexpr (sizeof(T) == 4) {
|
||||||
|
*reinterpret_cast<T*>(p) = bswap_u32(value);
|
||||||
|
} else if constexpr (sizeof(T) == 8) {
|
||||||
|
*reinterpret_cast<T*>(p) = bswap_u64(value);
|
||||||
} else {
|
} else {
|
||||||
*reinterpret_cast<uint64_t*>(p) = bswap_u64(value);
|
for (size_t i = 0; i < sizeof(T); i++) {
|
||||||
|
p[sizeof(T) - i - 1] = reinterpret_cast<const uint8_t*>(&value)[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint32_t be_u32_read(const uint8_t* p) {
|
template <typename T>
|
||||||
if constexpr (std::endian::native == std::endian::big) {
|
T le_read(const uint8_t* p)
|
||||||
return *reinterpret_cast<const uint32_t*>(p);
|
requires(std::is_integral_v<T>)
|
||||||
|
{
|
||||||
|
// ReSharper disable once CppDFAUnreachableCode
|
||||||
|
if constexpr (std::endian::native == std::endian::little) {
|
||||||
|
return *reinterpret_cast<const T*>(p);
|
||||||
|
} else if constexpr (sizeof(T) == 2) {
|
||||||
|
return bswap_u16(*reinterpret_cast<const T*>(p));
|
||||||
|
} else if constexpr (sizeof(T) == 4) {
|
||||||
|
return bswap_u32(*reinterpret_cast<const T*>(p));
|
||||||
|
} else if constexpr (sizeof(T) == 8) {
|
||||||
|
return bswap_u64(*reinterpret_cast<const T*>(p));
|
||||||
} else {
|
} else {
|
||||||
return bswap_u32(*reinterpret_cast<const uint32_t*>(p));
|
T result{};
|
||||||
|
for (size_t i = 0; i < sizeof(T); i++) {
|
||||||
|
reinterpret_cast<uint8_t*>(&result)[i] = p[i];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void be_u32_write(uint8_t* p, uint32_t value) {
|
template <typename T>
|
||||||
if constexpr (std::endian::native == std::endian::big) {
|
void le_write(uint8_t* p, const T value)
|
||||||
*reinterpret_cast<uint32_t*>(p) = value;
|
requires(std::is_integral_v<T>)
|
||||||
|
{
|
||||||
|
// ReSharper disable once CppDFAUnreachableCode
|
||||||
|
if constexpr (std::endian::native == std::endian::little) {
|
||||||
|
*reinterpret_cast<T*>(p) = value;
|
||||||
|
} else if constexpr (sizeof(T) == 2) {
|
||||||
|
*reinterpret_cast<T*>(p) = bswap_u16(value);
|
||||||
|
} else if constexpr (sizeof(T) == 4) {
|
||||||
|
*reinterpret_cast<T*>(p) = bswap_u32(value);
|
||||||
|
} else if constexpr (sizeof(T) == 8) {
|
||||||
|
*reinterpret_cast<T*>(p) = bswap_u64(value);
|
||||||
} else {
|
} else {
|
||||||
*reinterpret_cast<uint32_t*>(p) = bswap_u32(value);
|
for (size_t i = 0; i < sizeof(T); i++) {
|
||||||
|
p[i] = reinterpret_cast<const uint8_t*>(&value)[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
7
src/common/sqlite3_wrapper.h
Normal file
7
src/common/sqlite3_wrapper.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if __has_include(<sqlite3.h>)
|
||||||
|
#include <sqlite3.h>
|
||||||
|
#else
|
||||||
|
#include <winsqlite/winsqlite3.h>
|
||||||
|
#endif
|
38
src/common/str_helper.h
Normal file
38
src/common/str_helper.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace lsr {
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
typedef wchar_t character;
|
||||||
|
typedef std::wstring string;
|
||||||
|
typedef std::wstring_view string_view;
|
||||||
|
#define LSR_STR(x) L##x
|
||||||
|
|
||||||
|
inline void write_stderr(const string& msg) {
|
||||||
|
fputws(msg.c_str(), stderr);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
typedef char character;
|
||||||
|
typedef std::string string;
|
||||||
|
typedef std::string_view string_view;
|
||||||
|
#define LSR_STR(x) x
|
||||||
|
|
||||||
|
inline void write_stderr(const string& msg) {
|
||||||
|
fputs(msg.c_str(), stderr);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace lsr
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#define lsr___fprintf fwprintf
|
||||||
|
#else
|
||||||
|
#define lsr___fprintf fprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define lsr_eprintf(fmt, ...) lsr___fprintf(stderr, LSR_STR(fmt), __VA_ARGS__)
|
||||||
|
#define lsr_printf(fmt, ...) lsr___fprintf(stdout, LSR_STR(fmt), __VA_ARGS__)
|
@ -1,137 +1,176 @@
|
|||||||
#include "infra.h"
|
#include "infra.h"
|
||||||
#include "sqlite_error.h"
|
|
||||||
#include "sqlite_fn.h"
|
|
||||||
|
|
||||||
#include <windows.h>
|
#include <aes.h>
|
||||||
|
#include <endian_helper.h>
|
||||||
|
#include <md5.h>
|
||||||
|
#include <sqlite3_wrapper.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <fstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace Infra {
|
namespace Infra {
|
||||||
|
using std::size_t;
|
||||||
|
|
||||||
SqliteDB::SqliteDB(const std::filesystem::path& infra_dll_path) {
|
constexpr size_t kPageSize = 0x400;
|
||||||
ok_ = InitInfraDll(infra_dll_path);
|
|
||||||
|
inline bool is_valid_page_1_header(const uint8_t* page1) {
|
||||||
|
const auto o10 = Endian::le_read<uint32_t>(&page1[0x10]);
|
||||||
|
const auto o14 = Endian::le_read<uint32_t>(&page1[0x14]);
|
||||||
|
const uint32_t v6 = (o10 & 0xff) << 8 | (o10 & 0xff00) << 16;
|
||||||
|
return o14 == 0x20204000 && (v6 - 0x200) <= 0xFE00 && ((v6 - 1) & v6) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SqliteDB::Open(const std::filesystem::path& db_path, std::string_view key) {
|
void derive_page_key(uint8_t* aes_key, uint8_t* aes_iv, const uint8_t* p_master_key, const uint32_t page_no) {
|
||||||
if (infra_ == nullptr) {
|
std::array<uint8_t, 0x18> buffer{};
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto db_path_u8 = db_path.generic_u8string();
|
// Setup buffer
|
||||||
int rc = sqlite3_open_v2_(reinterpret_cast<const char*>(db_path_u8.c_str()), &db_, SQLITE_OPEN_READONLY, nullptr);
|
std::copy_n(p_master_key, 0x10, buffer.begin());
|
||||||
if (rc != SQLITE_OK) {
|
Endian::le_write(&buffer[0x10], page_no);
|
||||||
return false;
|
Endian::le_write(&buffer[0x14], 0x546C4173);
|
||||||
}
|
|
||||||
|
|
||||||
if (!key.empty()) {
|
// Derive Key
|
||||||
rc = sqlite3_key_(db_, key.data(), static_cast<int>(key.size()));
|
md5(aes_key, buffer.data(), buffer.size());
|
||||||
if (rc != SQLITE_OK) {
|
|
||||||
sqlite3_close_v2_(db_);
|
// Derive IV
|
||||||
db_ = nullptr;
|
for (uint32_t ebx{page_no + 1}, i = 0; i < 16; i += 4) {
|
||||||
return false;
|
uint32_t eax = 0x7FFFFF07 * (ebx / 0xce26);
|
||||||
|
uint32_t ecx = 0x9EF4 * ebx - eax;
|
||||||
|
if (ecx & 0x8000'0000) {
|
||||||
|
ecx += 0x7FFF'FF07;
|
||||||
}
|
}
|
||||||
|
ebx = ecx;
|
||||||
|
Endian::le_write(&buffer[i], ebx);
|
||||||
}
|
}
|
||||||
|
md5(aes_iv, buffer.data(), 0x10);
|
||||||
|
|
||||||
return true;
|
// Cleanup
|
||||||
|
std::ranges::fill(buffer, 0xcc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SqliteDB::Close() {
|
static const uint8_t kDefaultMasterKey[0x10] = {
|
||||||
if (db_) {
|
0x1d, 0x61, 0x31, 0x45, 0xb2, 0x47, 0xbf, 0x7f, //
|
||||||
sqlite3_close_v2_(db_);
|
0x3d, 0x18, 0x96, 0x72, 0x14, 0x4f, 0xe4, 0xbf, //
|
||||||
db_ = nullptr;
|
};
|
||||||
}
|
|
||||||
}
|
static constexpr std::array<uint8_t, 0x10> kSQLiteDatabaseHeader = { //
|
||||||
void SqliteDB::FreeInfraDll() {
|
'S', 'Q', 'L', 'i', 't', 'e', ' ', 'f', 'o', 'r', 'm', 'a', 't', ' ', '3', 0};
|
||||||
if (infra_ != nullptr) {
|
|
||||||
FreeLibrary(reinterpret_cast<HMODULE>(infra_));
|
int load_db(std::vector<uint8_t>& db_data, const std::filesystem::path& db_path) {
|
||||||
infra_ = nullptr;
|
using namespace AES;
|
||||||
|
db_data.clear();
|
||||||
|
|
||||||
|
std::ifstream ifs_db(db_path, std::ios::binary);
|
||||||
|
if (!ifs_db.is_open()) {
|
||||||
|
return SQLITE_CANTOPEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3_open_v2_ = nullptr;
|
ifs_db.seekg(0, std::ios::end);
|
||||||
sqlite3_key_ = nullptr;
|
const auto db_size = static_cast<size_t>(ifs_db.tellg());
|
||||||
sqlite3_prepare_v2_ = nullptr;
|
const auto last_page = db_size / kPageSize;
|
||||||
sqlite3_step_ = nullptr;
|
if (db_size % kPageSize != 0) {
|
||||||
sqlite3_column_text_ = nullptr;
|
return SQLITE_CORRUPT;
|
||||||
sqlite3_close_v2_ = nullptr;
|
}
|
||||||
sqlite3_finalize_ = nullptr;
|
ifs_db.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
db_data.resize(db_size);
|
||||||
|
auto p_page = db_data.data();
|
||||||
|
|
||||||
|
AES_ctx ctx_aes{};
|
||||||
|
for (size_t page_no = 1; page_no <= last_page; page_no++, p_page += kPageSize) {
|
||||||
|
ifs_db.read(reinterpret_cast<char*>(p_page), kPageSize);
|
||||||
|
if (!ifs_db) [[unlikely]] {
|
||||||
|
return SQLITE_IOERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
uint8_t aes_key[16];
|
||||||
|
uint8_t aes_iv[16];
|
||||||
|
derive_page_key(aes_key, aes_iv, kDefaultMasterKey, static_cast<uint32_t>(page_no));
|
||||||
|
AES_init_ctx_iv(&ctx_aes, aes_key, aes_iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page_no == 1) [[unlikely]] {
|
||||||
|
if (std::equal(kSQLiteDatabaseHeader.cbegin(), kSQLiteDatabaseHeader.cend(), p_page)) {
|
||||||
|
ifs_db.read(reinterpret_cast<char*>(p_page + kPageSize),
|
||||||
|
static_cast<std::streamsize>(db_size - kPageSize));
|
||||||
|
AES_cleanup(&ctx_aes);
|
||||||
|
return SQLITE_OK; // no encryption
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_valid_page_1_header(p_page)) {
|
||||||
|
AES_cleanup(&ctx_aes);
|
||||||
|
db_data.clear();
|
||||||
|
return SQLITE_CORRUPT; // header validation failed
|
||||||
|
}
|
||||||
|
std::array<uint8_t, 8> backup{}; // backup magic numbers
|
||||||
|
std::copy_n(&p_page[0x10], 0x08, backup.begin());
|
||||||
|
std::copy_n(&p_page[0x08], 0x08, &p_page[0x10]);
|
||||||
|
AES_CBC_decrypt_buffer(&ctx_aes, p_page + 0x10, kPageSize - 0x10);
|
||||||
|
if (!std::equal(backup.cbegin(), backup.cend(), &p_page[0x10])) {
|
||||||
|
db_data.clear();
|
||||||
|
return SQLITE_CORRUPT; // header validation failed
|
||||||
|
}
|
||||||
|
std::ranges::copy(kSQLiteDatabaseHeader, p_page);
|
||||||
|
} else {
|
||||||
|
AES_CBC_decrypt_buffer(&ctx_aes, p_page, kPageSize);
|
||||||
|
}
|
||||||
|
AES_cleanup(&ctx_aes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SqliteDB::InitInfraDll(const std::filesystem::path& infra_dll_path) {
|
int dump_ekey(kgm_ekey_db_t& result, const std::filesystem::path& db_path) {
|
||||||
auto path_unicode = infra_dll_path.wstring();
|
result.clear();
|
||||||
HMODULE hMod = LoadLibraryW(path_unicode.c_str());
|
|
||||||
infra_ = hMod;
|
|
||||||
if (hMod == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlite3_open_v2_ = reinterpret_cast<sqlite3_open_v2_t>(GetProcAddress(hMod, "sqlite3_open_v2"));
|
std::vector<uint8_t> db_data;
|
||||||
sqlite3_key_ = reinterpret_cast<sqlite3_key_t>(GetProcAddress(hMod, "sqlite3_key"));
|
int rc = load_db(db_data, db_path);
|
||||||
sqlite3_prepare_v2_ = reinterpret_cast<sqlite3_prepare_v2_t>(GetProcAddress(hMod, "sqlite3_prepare_v2"));
|
|
||||||
sqlite3_step_ = reinterpret_cast<sqlite3_step_t>(GetProcAddress(hMod, "sqlite3_step"));
|
|
||||||
sqlite3_column_text_ = reinterpret_cast<sqlite3_column_text_t>(GetProcAddress(hMod, "sqlite3_column_text"));
|
|
||||||
sqlite3_close_v2_ = reinterpret_cast<sqlite3_close_v2_t>(GetProcAddress(hMod, "sqlite3_close_v2"));
|
|
||||||
sqlite3_finalize_ = reinterpret_cast<sqlite3_finalize_t>(GetProcAddress(hMod, "sqlite3_finalize"));
|
|
||||||
|
|
||||||
if (!sqlite3_open_v2_ || !sqlite3_key_ || !sqlite3_prepare_v2_ || !sqlite3_step_ || !sqlite3_column_text_ ||
|
|
||||||
!sqlite3_close_v2_ || !sqlite3_finalize_) {
|
|
||||||
infra_ = nullptr;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
KugouDb::KugouDb(const std::filesystem::path& infra_dll_path, const std::filesystem::path& db_path)
|
|
||||||
: SqliteDB(infra_dll_path) {
|
|
||||||
int rc{-1};
|
|
||||||
if (!IsInfraOk()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Open(db_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
kgm_ekey_db_t KugouDb::dump_ekey(int& error) {
|
|
||||||
if (!IsOpen()) {
|
|
||||||
error = SQLITE_ERROR;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
int rc{-1};
|
|
||||||
sqlite3_stmt* stmt{nullptr};
|
|
||||||
|
|
||||||
rc = sqlite3_prepare_v2_(db_,
|
|
||||||
"select EncryptionKeyId, EncryptionKey from ShareFileItems"
|
|
||||||
" where EncryptionKey != ''",
|
|
||||||
-1, &stmt, nullptr);
|
|
||||||
if (rc != SQLITE_OK) {
|
if (rc != SQLITE_OK) {
|
||||||
error = rc;
|
return rc;
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
kgm_ekey_db_t result{};
|
// Open an in-memory database
|
||||||
while ((rc = sqlite3_step_(stmt)) == SQLITE_ROW) {
|
sqlite3* db = nullptr;
|
||||||
const auto* ekey_id = reinterpret_cast<const char*>(sqlite3_column_text_(stmt, 0));
|
rc = sqlite3_open(":memory:", &db);
|
||||||
const auto* ekey = reinterpret_cast<const char*>(sqlite3_column_text_(stmt, 1));
|
if (rc != SQLITE_OK) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto p_db_bytes = db_data.data();
|
||||||
|
const auto len = static_cast<sqlite3_int64>(db_data.size());
|
||||||
|
rc = sqlite3_deserialize(db, "main", p_db_bytes, len, len, SQLITE_DESERIALIZE_READONLY);
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
sqlite3_close(db);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_stmt* stmt{nullptr};
|
||||||
|
rc = sqlite3_prepare_v2(db,
|
||||||
|
"select EncryptionKeyId, EncryptionKey from ShareFileItems"
|
||||||
|
" where EncryptionKey != ''",
|
||||||
|
-1, &stmt, nullptr);
|
||||||
|
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
sqlite3_close(db);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
|
||||||
|
const auto* ekey_id = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
|
||||||
|
const auto* ekey = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
|
||||||
result[ekey_id] = ekey;
|
result[ekey_id] = ekey;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rc != SQLITE_DONE) {
|
if (rc != SQLITE_DONE) {
|
||||||
error = rc;
|
sqlite3_close(db);
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3_finalize_(stmt);
|
sqlite3_finalize(stmt);
|
||||||
error = 0;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
KugouDb::~KugouDb() {
|
return sqlite3_close(db);
|
||||||
if (db_ != nullptr) {
|
|
||||||
sqlite3_close_v2_(db_);
|
|
||||||
db_ = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KugouDb::Open(const std::filesystem::path& db_path) {
|
|
||||||
return SqliteDB::Open(db_path, {"7777B48756BA491BB4CEE771A3E2727E"});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Infra
|
} // namespace Infra
|
||||||
|
@ -3,47 +3,10 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "sqlite_base.h"
|
|
||||||
#include "sqlite_fn.h"
|
|
||||||
|
|
||||||
namespace Infra {
|
namespace Infra {
|
||||||
|
|
||||||
typedef std::unordered_map<std::string, std::string> kgm_ekey_db_t;
|
typedef std::unordered_map<std::string, std::string> kgm_ekey_db_t;
|
||||||
extern bool g_init_sqlite_ok;
|
|
||||||
|
|
||||||
class SqliteDB {
|
int dump_ekey(kgm_ekey_db_t& result, const std::filesystem::path& db_path);
|
||||||
public:
|
|
||||||
explicit SqliteDB(const std::filesystem::path& infra_dll_path);
|
|
||||||
bool Open(const std::filesystem::path& db_path, std::string_view key);
|
|
||||||
void Close();
|
|
||||||
[[nodiscard]] bool IsInfraOk() const { return ok_; }
|
|
||||||
[[nodiscard]] bool IsOpen() const { return db_ != nullptr; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool InitInfraDll(const std::filesystem::path& infra_dll_path);
|
|
||||||
bool ok_{false};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void FreeInfraDll();
|
|
||||||
|
|
||||||
void* infra_{nullptr};
|
|
||||||
sqlite3_open_v2_t sqlite3_open_v2_{nullptr};
|
|
||||||
sqlite3_key_t sqlite3_key_{nullptr};
|
|
||||||
sqlite3_prepare_v2_t sqlite3_prepare_v2_{nullptr};
|
|
||||||
sqlite3_step_t sqlite3_step_{nullptr};
|
|
||||||
sqlite3_column_text_t sqlite3_column_text_{nullptr};
|
|
||||||
sqlite3_close_v2_t sqlite3_close_v2_{nullptr};
|
|
||||||
sqlite3_finalize_t sqlite3_finalize_{nullptr};
|
|
||||||
sqlite3* db_{nullptr};
|
|
||||||
};
|
|
||||||
|
|
||||||
class KugouDb : public SqliteDB {
|
|
||||||
public:
|
|
||||||
explicit KugouDb(const std::filesystem::path& infra_dll_path, const std::filesystem::path& db_path);
|
|
||||||
~KugouDb();
|
|
||||||
|
|
||||||
bool Open(const std::filesystem::path& db_path);
|
|
||||||
kgm_ekey_db_t dump_ekey(int& error);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Infra
|
} // namespace Infra
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// SQLite
|
|
||||||
#define SQLITE_OPEN_READONLY 0x00000001 /* Ok for sqlite3_open_v2() */
|
|
||||||
#define SQLITE_OPEN_READWRITE 0x00000002 /* Ok for sqlite3_open_v2() */
|
|
||||||
#define SQLITE_OPEN_CREATE 0x00000004 /* Ok for sqlite3_open_v2() */
|
|
||||||
|
|
||||||
typedef struct sqlite3 sqlite3;
|
|
||||||
typedef struct sqlite3_stmt sqlite3_stmt;
|
|
@ -1,99 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#define SQLITE_OK (0)
|
|
||||||
|
|
||||||
/* beginning-of-error-codes */
|
|
||||||
#define SQLITE_ERROR 1 /* Generic error */
|
|
||||||
#define SQLITE_INTERNAL 2 /* Internal logic error in SQLite */
|
|
||||||
#define SQLITE_PERM 3 /* Access permission denied */
|
|
||||||
#define SQLITE_ABORT 4 /* Callback routine requested an abort */
|
|
||||||
#define SQLITE_BUSY 5 /* The database file is locked */
|
|
||||||
#define SQLITE_LOCKED 6 /* A table in the database is locked */
|
|
||||||
#define SQLITE_NOMEM 7 /* A malloc() failed */
|
|
||||||
#define SQLITE_READONLY 8 /* Attempt to write a readonly database */
|
|
||||||
#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/
|
|
||||||
#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */
|
|
||||||
#define SQLITE_CORRUPT 11 /* The database disk image is malformed */
|
|
||||||
#define SQLITE_NOTFOUND 12 /* Unknown opcode in sqlite3_file_control() */
|
|
||||||
#define SQLITE_FULL 13 /* Insertion failed because database is full */
|
|
||||||
#define SQLITE_CANTOPEN 14 /* Unable to open the database file */
|
|
||||||
#define SQLITE_PROTOCOL 15 /* Database lock protocol error */
|
|
||||||
#define SQLITE_EMPTY 16 /* Internal use only */
|
|
||||||
#define SQLITE_SCHEMA 17 /* The database schema changed */
|
|
||||||
#define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */
|
|
||||||
#define SQLITE_CONSTRAINT 19 /* Abort due to constraint violation */
|
|
||||||
#define SQLITE_MISMATCH 20 /* Data type mismatch */
|
|
||||||
#define SQLITE_MISUSE 21 /* Library used incorrectly */
|
|
||||||
#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */
|
|
||||||
#define SQLITE_AUTH 23 /* Authorization denied */
|
|
||||||
#define SQLITE_FORMAT 24 /* Not used */
|
|
||||||
#define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */
|
|
||||||
#define SQLITE_NOTADB 26 /* File opened that is not a database file */
|
|
||||||
#define SQLITE_NOTICE 27 /* Notifications from sqlite3_log() */
|
|
||||||
#define SQLITE_WARNING 28 /* Warnings from sqlite3_log() */
|
|
||||||
#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */
|
|
||||||
#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */
|
|
||||||
/* end-of-error-codes */
|
|
||||||
|
|
||||||
inline const char* sqlite_get_error(int rc) {
|
|
||||||
switch (rc) {
|
|
||||||
case SQLITE_ERROR:
|
|
||||||
return "SQLITE_ERROR: Generic error";
|
|
||||||
case SQLITE_INTERNAL:
|
|
||||||
return "SQLITE_INTERNAL: Internal logic error in SQLite";
|
|
||||||
case SQLITE_PERM:
|
|
||||||
return "SQLITE_PERM: Access permission denied";
|
|
||||||
case SQLITE_ABORT:
|
|
||||||
return "SQLITE_ABORT: Callback routine requested an abort";
|
|
||||||
case SQLITE_BUSY:
|
|
||||||
return "SQLITE_BUSY: The database file is locked";
|
|
||||||
case SQLITE_LOCKED:
|
|
||||||
return "SQLITE_LOCKED: A table in the database is locked";
|
|
||||||
case SQLITE_NOMEM:
|
|
||||||
return "SQLITE_NOMEM: A malloc() failed";
|
|
||||||
case SQLITE_READONLY:
|
|
||||||
return "SQLITE_READONLY: Attempt to write a readonly database";
|
|
||||||
case SQLITE_INTERRUPT:
|
|
||||||
return "SQLITE_INTERRUPT: Operation terminated by sqlite3_interrupt()";
|
|
||||||
case SQLITE_IOERR:
|
|
||||||
return "SQLITE_IOERR: Some kind of disk I/O error occurred";
|
|
||||||
case SQLITE_CORRUPT:
|
|
||||||
return "SQLITE_CORRUPT: The database disk image is malformed";
|
|
||||||
case SQLITE_NOTFOUND:
|
|
||||||
return "SQLITE_NOTFOUND: Unknown opcode in sqlite3_file_control()";
|
|
||||||
case SQLITE_FULL:
|
|
||||||
return "SQLITE_FULL: Insertion failed because database is full";
|
|
||||||
case SQLITE_CANTOPEN:
|
|
||||||
return "SQLITE_CANTOPEN: Unable to open the database file";
|
|
||||||
case SQLITE_PROTOCOL:
|
|
||||||
return "SQLITE_PROTOCOL: Database lock protocol error";
|
|
||||||
case SQLITE_EMPTY:
|
|
||||||
return "SQLITE_EMPTY: Internal use only";
|
|
||||||
case SQLITE_SCHEMA:
|
|
||||||
return "SQLITE_SCHEMA: The database schema changed";
|
|
||||||
case SQLITE_TOOBIG:
|
|
||||||
return "SQLITE_TOOBIG: String or BLOB exceeds size limit";
|
|
||||||
case SQLITE_CONSTRAINT:
|
|
||||||
return "SQLITE_CONSTRAINT: Abort due to constraint violation";
|
|
||||||
case SQLITE_MISMATCH:
|
|
||||||
return "SQLITE_MISMATCH: Data type mismatch";
|
|
||||||
case SQLITE_MISUSE:
|
|
||||||
return "SQLITE_MISUSE: Library used incorrectly";
|
|
||||||
case SQLITE_NOLFS:
|
|
||||||
return "SQLITE_NOLFS: Uses OS features not supported on host";
|
|
||||||
case SQLITE_AUTH:
|
|
||||||
return "SQLITE_AUTH: Authorization denied";
|
|
||||||
case SQLITE_FORMAT:
|
|
||||||
return "SQLITE_FORMAT: Not used";
|
|
||||||
case SQLITE_RANGE:
|
|
||||||
return "SQLITE_RANGE: 2nd parameter to sqlite3_bind out of range";
|
|
||||||
case SQLITE_NOTADB:
|
|
||||||
return "SQLITE_NOTADB: File opened that is not a database file";
|
|
||||||
case SQLITE_NOTICE:
|
|
||||||
return "SQLITE_NOTICE: Notifications from sqlite3_log()";
|
|
||||||
case SQLITE_WARNING:
|
|
||||||
return "SQLITE_WARNING: Warnings from sqlite3_log()";
|
|
||||||
default:
|
|
||||||
return "<unknown>";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "sqlite_base.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Opens a SQLite database file with extended options.
|
|
||||||
*
|
|
||||||
* @param filename The name of the database file to be opened (UTF-8 encoded).
|
|
||||||
* @param ppDb A pointer to a pointer that will receive the SQLite database
|
|
||||||
* handle upon successful opening.
|
|
||||||
* @param flags Flags that control the behavior of the database connection.
|
|
||||||
* @param zVfs The name of the VFS (Virtual File System) module to use.
|
|
||||||
* If NULL, the default VFS is used.
|
|
||||||
* @return Returns SQLITE_OK on success or an error code on failure.
|
|
||||||
*/
|
|
||||||
typedef int (*sqlite3_open_v2_t)(const char* filename, sqlite3** ppDb, int flags, const char* zVfs);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Compiles an SQL statement into a prepared statement.
|
|
||||||
*
|
|
||||||
* @param db Database handle.
|
|
||||||
* @param zSql SQL statement, UTF-8 encoded.
|
|
||||||
* @param n Maximum length of zSql in bytes.
|
|
||||||
* @param ppStmt OUT: Statement handle.
|
|
||||||
* @param pzTail OUT: Pointer to unused portion of zSql.
|
|
||||||
* @return Returns SQLITE_OK on success or an error code on failure.
|
|
||||||
*/
|
|
||||||
typedef int (*sqlite3_prepare_v2_t)(sqlite3* db, const char* zSql, int n, sqlite3_stmt** ppStmt, const char** pzTail);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Evaluates a prepared statement.
|
|
||||||
*
|
|
||||||
* @param stmt Prepared statement.
|
|
||||||
* @return Returns SQLITE_ROW, SQLITE_DONE, or an error code.
|
|
||||||
*/
|
|
||||||
typedef int (*sqlite3_step_t)(sqlite3_stmt* stmt);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns the text value of a column in the current row of a result set.
|
|
||||||
*
|
|
||||||
* @param stmt Prepared statement.
|
|
||||||
* @param iCol Column index.
|
|
||||||
* @return Text value of the column.
|
|
||||||
*/
|
|
||||||
typedef const unsigned char* (*sqlite3_column_text_t)(sqlite3_stmt* stmt, int iCol);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destroys a prepared statement object.
|
|
||||||
*
|
|
||||||
* @param stmt Prepared statement.
|
|
||||||
* @return Returns SQLITE_OK on success or an error code on failure.
|
|
||||||
*/
|
|
||||||
typedef int (*sqlite3_finalize_t)(sqlite3_stmt* stmt);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Closes a database connection and invalidates all prepared statements.
|
|
||||||
*
|
|
||||||
* @param db Database handle.
|
|
||||||
* @return Returns SQLITE_OK on success or an error code on failure.
|
|
||||||
*/
|
|
||||||
typedef int (*sqlite3_close_v2_t)(sqlite3* db);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Sets the encryption key for a database.
|
|
||||||
*
|
|
||||||
* @param db Database to be keyed.
|
|
||||||
* @param pKey The key.
|
|
||||||
* @param nKey The length of the key in bytes.
|
|
||||||
* @return Returns SQLITE_OK on success or an error code on failure.
|
|
||||||
*/
|
|
||||||
typedef int (*sqlite3_key_t)(sqlite3* db, const void* pKey, int nKey);
|
|
129
src/jobs.hpp
129
src/jobs.hpp
@ -1,52 +1,58 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include "infra/infra.h"
|
||||||
|
#include "qmc2/qmc2.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <pthread.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <endian_helper.h>
|
||||||
|
#include <str_helper.h>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <condition_variable>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "qmc2/qmc2.h"
|
|
||||||
|
|
||||||
class KggTask {
|
class KggTask {
|
||||||
public:
|
public:
|
||||||
explicit KggTask(std::filesystem::path kgg_path, std::filesystem::path out_dir)
|
explicit KggTask(std::filesystem::path kgg_path, std::filesystem::path out_dir)
|
||||||
: kgg_path_(std::move(kgg_path)), out_dir_(std::move(out_dir)) {}
|
: kgg_path_(std::move(kgg_path)), out_dir_(std::move(out_dir)) {}
|
||||||
|
|
||||||
void warning(const wchar_t* msg) const { fwprintf(stderr, L"[WARN] %s (%s)\n", msg, kgg_path_.filename().c_str()); }
|
void log(const lsr::string& level, const lsr::string& msg) const {
|
||||||
void warning(const std::wstring& msg) const { warning(msg.c_str()); }
|
lsr_eprintf("[%s] %s (%s)\n", level.c_str(), msg.c_str(), kgg_path_.filename().c_str());
|
||||||
|
}
|
||||||
|
void warning(const lsr::string& msg) const { log(LSR_STR("WARN"), msg); }
|
||||||
|
void error(const lsr::string& msg) const { log(LSR_STR("ERR "), msg); }
|
||||||
|
void info(const lsr::string& msg) const { log(LSR_STR("INFO"), msg); }
|
||||||
|
|
||||||
void error(const wchar_t* msg) const { fwprintf(stderr, L"[ERR ] %s (%s)\n", msg, kgg_path_.filename().c_str()); }
|
void Execute(const Infra::kgm_ekey_db_t& ekey_db, const lsr::string_view suffix) const {
|
||||||
void error(const std::wstring& msg) const { error(msg.c_str()); }
|
|
||||||
|
|
||||||
void info(const wchar_t* msg) const { fwprintf(stderr, L"[INFO] %s (%s)\n", msg, kgg_path_.filename().c_str()); }
|
|
||||||
void info(const std::wstring& msg) const { info(msg.c_str()); }
|
|
||||||
|
|
||||||
void Execute(const Infra::kgm_ekey_db_t& ekey_db, const std::wstring_view suffix) const {
|
|
||||||
constexpr static std::array<uint8_t, 16> kMagicHeader{0x7C, 0xD5, 0x32, 0xEB, 0x86, 0x02, 0x7F, 0x4B,
|
constexpr static std::array<uint8_t, 16> kMagicHeader{0x7C, 0xD5, 0x32, 0xEB, 0x86, 0x02, 0x7F, 0x4B,
|
||||||
0xA8, 0xAF, 0xA6, 0x8E, 0x0F, 0xFF, 0x99, 0x14};
|
0xA8, 0xAF, 0xA6, 0x8E, 0x0F, 0xFF, 0x99, 0x14};
|
||||||
|
|
||||||
std::ifstream kgg_stream_in(kgg_path_, std::ios::binary);
|
std::ifstream kgg_stream_in(kgg_path_, std::ios::binary);
|
||||||
char header[0x100]{};
|
std::array<uint8_t, 0x400> header{};
|
||||||
kgg_stream_in.read(header, sizeof(kgg_stream_in));
|
kgg_stream_in.read(reinterpret_cast<char*>(header.data()), header.size());
|
||||||
if (std::equal(kMagicHeader.cbegin(), kMagicHeader.cend(), header)) {
|
if (!std::equal(kMagicHeader.cbegin(), kMagicHeader.cend(), header.cbegin())) {
|
||||||
warning(L"invalid kgg header");
|
warning(LSR_STR("invalid kgg header (not a kgg file)"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const uint32_t offset_to_audio = *reinterpret_cast<uint32_t*>(&header[0x10]);
|
const auto offset_to_audio = Endian::le_read<uint32_t>(&header[0x10]);
|
||||||
const uint32_t encrypt_mode = *reinterpret_cast<uint32_t*>(&header[0x14]);
|
if (const auto mode = Endian::le_read<uint32_t>(&header[0x14]); mode != 5) {
|
||||||
if (encrypt_mode != 5) {
|
lsr_eprintf("[WARN] unsupported enc_version (expect=0x05, got 0x%02x) (%s)\n", mode,
|
||||||
warning(std::format(L"unsupported enc_version (expect=0x05, got 0x{:02x})", encrypt_mode));
|
kgg_path_.filename().c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint32_t audio_hash_len = *reinterpret_cast<uint32_t*>(&header[0x44]);
|
uint32_t audio_hash_len = *reinterpret_cast<uint32_t*>(&header[0x44]);
|
||||||
if (audio_hash_len != 0x20) {
|
if (audio_hash_len != 0x20) {
|
||||||
warning(std::format(L"audio hash length invalid (expect=0x20, got 0x{:02x})", audio_hash_len));
|
lsr_eprintf("audio hash length invalid (expect=0x20, got 0x%02x) (%s)\n", audio_hash_len,
|
||||||
|
kgg_path_.filename().c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::string audio_hash(&header[0x48], &header[0x48 + audio_hash_len]);
|
std::string audio_hash(&header[0x48], &header[0x48 + audio_hash_len]);
|
||||||
@ -54,70 +60,77 @@ class KggTask {
|
|||||||
if (auto it = ekey_db.find(audio_hash); it != ekey_db.end()) {
|
if (auto it = ekey_db.find(audio_hash); it != ekey_db.end()) {
|
||||||
ekey = it->second;
|
ekey = it->second;
|
||||||
} else {
|
} else {
|
||||||
warning(L"ekey not found");
|
warning(LSR_STR("ekey not found"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto qmc2 = QMC2::Create(ekey);
|
auto qmc2 = QMC2::Create(ekey);
|
||||||
if (!qmc2) {
|
if (!qmc2) {
|
||||||
error(L"create qmc2 instance failed (ekey decode error?)");
|
error(LSR_STR("create qmc2 instance failed (ekey decode error?)"));
|
||||||
fprintf(stderr, "%s\n", ekey.c_str());
|
fprintf(stderr, "%s\n", ekey.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string magic(4, 0);
|
std::array<uint8_t, 4> magic{};
|
||||||
kgg_stream_in.seekg(offset_to_audio, std::ios::beg);
|
kgg_stream_in.seekg(offset_to_audio, std::ios::beg);
|
||||||
kgg_stream_in.read(magic.data(), 4);
|
kgg_stream_in.read(reinterpret_cast<char*>(magic.data()), 4);
|
||||||
qmc2->Decrypt(std::span(reinterpret_cast<uint8_t*>(magic.data()), 4), 0);
|
qmc2->Decrypt(magic, 0);
|
||||||
auto real_ext = DetectRealExt(magic);
|
auto real_ext = DetectRealExt(magic);
|
||||||
auto out_path = out_dir_ / std::format(L"{}{}.{}", kgg_path_.stem().wstring(), suffix, real_ext);
|
|
||||||
|
lsr::string new_name = kgg_path_.stem().native() + lsr::string(suffix) + LSR_STR(".") + real_ext;
|
||||||
|
auto out_path = out_dir_ / new_name;
|
||||||
|
|
||||||
if (exists(out_path)) {
|
if (exists(out_path)) {
|
||||||
warning(std::format(L"output file already exists: {}", out_path.filename().wstring()));
|
warning(lsr::string(LSR_STR("output file already exists: ")) + new_name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kgg_stream_in.seekg(0, std::ios::end);
|
||||||
|
const auto file_size = static_cast<size_t>(kgg_stream_in.tellg());
|
||||||
kgg_stream_in.seekg(offset_to_audio, std::ios::beg);
|
kgg_stream_in.seekg(offset_to_audio, std::ios::beg);
|
||||||
std::ofstream ofs_decrypted(out_path, std::ios::binary);
|
std::ofstream ofs_decrypted(out_path, std::ios::binary);
|
||||||
if (!ofs_decrypted.is_open()) {
|
if (!ofs_decrypted.is_open()) {
|
||||||
error(L"failed to open output file");
|
error(LSR_STR("failed to open output file"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t offset{0};
|
size_t offset{0};
|
||||||
thread_local std::vector<uint8_t> temp_buffer(1024 * 1024, 0);
|
thread_local std::vector<uint8_t> temp_buffer(1024 * 1024, 0);
|
||||||
auto buf_signed = std::span(reinterpret_cast<char*>(temp_buffer.data()), temp_buffer.size());
|
auto read_page_len = static_cast<std::streamsize>(temp_buffer.size());
|
||||||
auto buf_unsigned = std::span(temp_buffer);
|
|
||||||
|
|
||||||
while (!kgg_stream_in.eof()) {
|
while (!kgg_stream_in.eof()) {
|
||||||
kgg_stream_in.read(buf_signed.data(), buf_signed.size());
|
kgg_stream_in.read(reinterpret_cast<char*>(temp_buffer.data()), read_page_len);
|
||||||
const auto n = static_cast<size_t>(kgg_stream_in.gcount());
|
const auto n = kgg_stream_in.gcount();
|
||||||
qmc2->Decrypt(buf_unsigned.subspan(0, n), offset);
|
qmc2->Decrypt(std::span(temp_buffer.begin(), temp_buffer.begin() + n), offset);
|
||||||
ofs_decrypted.write(buf_signed.data(), n);
|
ofs_decrypted.write(reinterpret_cast<char*>(temp_buffer.data()), n);
|
||||||
offset += n;
|
offset += n;
|
||||||
}
|
}
|
||||||
|
|
||||||
info(std::format(L"** OK ** -> {}", out_path.filename().wstring()));
|
if (offset + offset_to_audio != file_size) {
|
||||||
|
warning(LSR_STR("OK (size mismatch)"));
|
||||||
|
} else {
|
||||||
|
info(lsr::string(LSR_STR("** OK ** -> ")) + out_path.filename().native());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::filesystem::path kgg_path_;
|
std::filesystem::path kgg_path_;
|
||||||
std::filesystem::path out_dir_;
|
std::filesystem::path out_dir_;
|
||||||
|
|
||||||
static const wchar_t* DetectRealExt(const std::string_view magic) {
|
static const lsr::character* DetectRealExt(const std::span<uint8_t> magic) {
|
||||||
if (magic == "fLaC") {
|
if (std::equal(magic.begin(), magic.end(), "fLaC")) {
|
||||||
return L"flac";
|
return LSR_STR("flac");
|
||||||
}
|
}
|
||||||
if (magic == "OggS") {
|
if (std::equal(magic.begin(), magic.end(), "OggS")) {
|
||||||
return L"ogg";
|
return LSR_STR("ogg");
|
||||||
}
|
}
|
||||||
return L"mp3";
|
return LSR_STR("mp3");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class KggTaskQueue {
|
class KggTaskQueue {
|
||||||
public:
|
public:
|
||||||
explicit KggTaskQueue(Infra::kgm_ekey_db_t ekey_db, const std::wstring_view suffix)
|
explicit KggTaskQueue(Infra::kgm_ekey_db_t ekey_db, const lsr::string_view suffix)
|
||||||
: ekey_db_(std::move(ekey_db)), suffix_(suffix) {}
|
: ekey_db_(std::move(ekey_db)), suffix_(suffix) {}
|
||||||
|
|
||||||
void Push(std::unique_ptr<KggTask> task) {
|
void Push(std::unique_ptr<KggTask> task) {
|
||||||
@ -161,16 +174,34 @@ class KggTaskQueue {
|
|||||||
private:
|
private:
|
||||||
bool thread_end_{false};
|
bool thread_end_{false};
|
||||||
Infra::kgm_ekey_db_t ekey_db_;
|
Infra::kgm_ekey_db_t ekey_db_;
|
||||||
std::wstring suffix_;
|
lsr::string suffix_;
|
||||||
void WorkerThreadBody() {
|
void WorkerThreadBody() {
|
||||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
|
ReduceThreadPriority();
|
||||||
|
|
||||||
std::unique_ptr<KggTask> task{nullptr};
|
std::unique_ptr<KggTask> task{nullptr};
|
||||||
while ((task = Pop())) {
|
while ((task = Pop())) {
|
||||||
task->Execute(ekey_db_, suffix_);
|
task->Execute(ekey_db_, suffix_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ReduceThreadPriority() {
|
||||||
|
#ifdef _WIN32
|
||||||
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
|
||||||
|
#else
|
||||||
|
pthread_t this_thread = pthread_self();
|
||||||
|
sched_param params;
|
||||||
|
int policy;
|
||||||
|
|
||||||
|
if (pthread_getschedparam(this_thread, &policy, ¶ms) != 0) {
|
||||||
|
perror("pthread_getschedparam");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
params.sched_priority = std::max(params.sched_priority, sched_get_priority_min(policy));
|
||||||
|
if (pthread_setschedparam(this_thread, policy, ¶ms) != 0) {
|
||||||
|
perror("pthread_setschedparam");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
std::mutex mutex_{};
|
std::mutex mutex_{};
|
||||||
std::condition_variable signal_;
|
std::condition_variable signal_;
|
||||||
std::queue<std::unique_ptr<KggTask>> tasks_{};
|
std::queue<std::unique_ptr<KggTask>> tasks_{};
|
||||||
|
34
src/main.cpp
34
src/main.cpp
@ -1,6 +1,11 @@
|
|||||||
|
#include <sqlite3_wrapper.h>
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
#include "infra/infra.h"
|
#include "infra/infra.h"
|
||||||
#include "infra/sqlite_error.h"
|
|
||||||
#include "jobs.hpp"
|
#include "jobs.hpp"
|
||||||
|
#include "str_helper.h"
|
||||||
#include "utils/cli.h"
|
#include "utils/cli.h"
|
||||||
|
|
||||||
using Infra::kgm_ekey_db_t;
|
using Infra::kgm_ekey_db_t;
|
||||||
@ -29,7 +34,7 @@ void WalkFileOrDir(KggTaskQueue& queue, const std::filesystem::path& input_path,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
fwprintf(stderr, L"[WARN] invalid path: %s\n", target_path.c_str());
|
lsr_eprintf("[WARN] invalid path: %s\n", target_path.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +46,6 @@ void print_license() {
|
|||||||
void print_usage() {
|
void print_usage() {
|
||||||
fputs(
|
fputs(
|
||||||
"Usage: kgg-dec "
|
"Usage: kgg-dec "
|
||||||
"[--infra-dll infra.dll] "
|
|
||||||
"[--scan-all-file-ext 0] "
|
"[--scan-all-file-ext 0] "
|
||||||
"[--db /path/to/KGMusicV3.db] "
|
"[--db /path/to/KGMusicV3.db] "
|
||||||
"[--suffix _kgg-dec] "
|
"[--suffix _kgg-dec] "
|
||||||
@ -56,23 +60,18 @@ void print_banner() {
|
|||||||
print_usage();
|
print_usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main(int argc, char** argv) {
|
||||||
CliParser cli_args;
|
CliParser cli_args;
|
||||||
print_banner();
|
print_banner();
|
||||||
|
|
||||||
cli_args.parse_from_cli();
|
cli_args.parse_from_cli(argc, argv);
|
||||||
|
|
||||||
bool scan_all_exts = cli_args.get_scan_all_file_ext();
|
bool scan_all_exts = cli_args.get_scan_all_file_ext();
|
||||||
|
|
||||||
auto infra_dll_path = cli_args.get_infra_dll();
|
|
||||||
auto kgm_db_path = cli_args.get_db_path();
|
auto kgm_db_path = cli_args.get_db_path();
|
||||||
auto file_suffix = cli_args.get_file_suffix();
|
auto file_suffix = cli_args.get_file_suffix();
|
||||||
{
|
{
|
||||||
bool cli_arg_error{false};
|
bool cli_arg_error{false};
|
||||||
if (!exists(infra_dll_path)) {
|
|
||||||
fputs("[ERR ] infra.dll not found\n", stderr);
|
|
||||||
cli_arg_error = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!exists(kgm_db_path)) {
|
if (!exists(kgm_db_path)) {
|
||||||
fputs("[ERR ] KGMusicV3.db not found\n", stderr);
|
fputs("[ERR ] KGMusicV3.db not found\n", stderr);
|
||||||
@ -83,18 +82,11 @@ int main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int error{-1};
|
kgm_ekey_db_t ekey_db;
|
||||||
Infra::KugouDb db{infra_dll_path, kgm_db_path};
|
if (const auto rc = Infra::dump_ekey(ekey_db, kgm_db_path); rc != 0) {
|
||||||
if (!db.IsOpen()) {
|
fprintf(stderr, "[ERR ] dump ekey failed %d (%s)", rc, sqlite3_errstr(rc));
|
||||||
fprintf(stderr, "[ERR ] db init error: is infra.dll ok?\n");
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
auto ekey_db = db.dump_ekey(error);
|
|
||||||
if (error != 0) {
|
|
||||||
fprintf(stderr, "[ERR ] dump ekey failed %d (%s)", error, sqlite_get_error(error));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
db.Close();
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
fprintf(stderr, "ekey_db:\n");
|
fprintf(stderr, "ekey_db:\n");
|
||||||
@ -117,7 +109,7 @@ int main() {
|
|||||||
|
|
||||||
auto input_files = cli_args.get_input_files();
|
auto input_files = cli_args.get_input_files();
|
||||||
if (input_files.empty()) {
|
if (input_files.empty()) {
|
||||||
input_files.emplace_back(L".");
|
input_files.emplace_back(LSR_STR("."));
|
||||||
}
|
}
|
||||||
for (auto& positional_arg : input_files) {
|
for (auto& positional_arg : input_files) {
|
||||||
WalkFileOrDir(queue, positional_arg, scan_all_exts);
|
WalkFileOrDir(queue, positional_arg, scan_all_exts);
|
||||||
|
@ -15,12 +15,12 @@ inline void decrypt_round(uint8_t* p_plain,
|
|||||||
uint64_t* iv1,
|
uint64_t* iv1,
|
||||||
uint64_t* iv2,
|
uint64_t* iv2,
|
||||||
const uint32_t* key) {
|
const uint32_t* key) {
|
||||||
uint64_t iv1_next = Endian::be_u64_read(p_cipher);
|
uint64_t iv1_next = Endian::be_read<uint64_t>(p_cipher);
|
||||||
uint64_t iv2_next = tc_tea_ecb_decrypt(iv1_next ^ *iv2, key);
|
uint64_t iv2_next = tc_tea_ecb_decrypt(iv1_next ^ *iv2, key);
|
||||||
uint64_t plain = iv2_next ^ *iv1;
|
uint64_t plain = iv2_next ^ *iv1;
|
||||||
*iv1 = iv1_next;
|
*iv1 = iv1_next;
|
||||||
*iv2 = iv2_next;
|
*iv2 = iv2_next;
|
||||||
Endian::be_u64_write(p_plain, plain);
|
Endian::be_write(p_plain, plain);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> tc_tea_cbc_decrypt(std::span<uint8_t> cipher, const uint32_t* key) {
|
std::vector<uint8_t> tc_tea_cbc_decrypt(std::span<uint8_t> cipher, const uint32_t* key) {
|
||||||
@ -62,7 +62,7 @@ std::vector<uint8_t> tc_tea_cbc_decrypt(std::span<uint8_t> cipher, const uint32_
|
|||||||
p_output[0] = header[kTeaBlockSize];
|
p_output[0] = header[kTeaBlockSize];
|
||||||
}
|
}
|
||||||
// Validate zero padding
|
// Validate zero padding
|
||||||
auto verify = Endian::be_u64_read(header + kTeaBlockSize) << 8;
|
auto verify = Endian::be_read<uint64_t>(header + kTeaBlockSize) << 8;
|
||||||
if (verify != 0) {
|
if (verify != 0) {
|
||||||
result.resize(0);
|
result.resize(0);
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@ std::vector<uint8_t> tc_tea_cbc_decrypt(std::span<uint8_t> cipher, const uint32_
|
|||||||
|
|
||||||
inline std::vector<uint8_t> tc_tea_cbc_decrypt(std::span<uint8_t> cipher, const uint8_t* key) {
|
inline std::vector<uint8_t> tc_tea_cbc_decrypt(std::span<uint8_t> cipher, const uint8_t* key) {
|
||||||
uint32_t key_u32[4];
|
uint32_t key_u32[4];
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++, key += 4) {
|
||||||
key_u32[i] = Endian::be_u32_read(key + i * 4);
|
key_u32[i] = Endian::be_read<uint32_t>(key);
|
||||||
}
|
}
|
||||||
return tc_tea_cbc_decrypt(cipher, key_u32);
|
return tc_tea_cbc_decrypt(cipher, key_u32);
|
||||||
}
|
}
|
||||||
|
@ -1,66 +1,77 @@
|
|||||||
#include "cli.h"
|
#include "cli.h"
|
||||||
|
#include <str_helper.h>
|
||||||
|
|
||||||
#include <Windows.h>
|
#ifdef _WIN32
|
||||||
#include <clocale>
|
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
#include <clocale>
|
||||||
|
#include <Windows.h>
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
#include <knownfolders.h>
|
#include <knownfolders.h>
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
#endif
|
||||||
|
|
||||||
CliParser::CliParser() {
|
CliParser::CliParser() {
|
||||||
|
#ifdef _WIN32
|
||||||
SetConsoleOutputCP(CP_UTF8);
|
SetConsoleOutputCP(CP_UTF8);
|
||||||
setlocale(LC_ALL, ".UTF8");
|
setlocale(LC_ALL, ".UTF8");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void CliParser::parse_from_cli() {
|
void CliParser::parse_from_cli(int argc_, char** argv_) {
|
||||||
int argc;
|
int argc{argc_};
|
||||||
|
#ifdef _WIN32
|
||||||
wchar_t** argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
wchar_t** argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
||||||
|
#else
|
||||||
|
char**& argv = argv_;
|
||||||
|
#endif
|
||||||
|
|
||||||
std::vector<std::wstring> positional_args{};
|
std::vector<lsr::string> positional_args{};
|
||||||
std::unordered_map<std::wstring, std::wstring> named_args{};
|
std::unordered_map<lsr::string, lsr::string> named_args{};
|
||||||
|
|
||||||
bool positional_only{false};
|
bool positional_only{false};
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
std::wstring arg{argv[i]};
|
lsr::string arg{argv[i]};
|
||||||
if (arg == L"--") {
|
if (arg == LSR_STR("--")) {
|
||||||
positional_only = true;
|
positional_only = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!positional_only && arg.starts_with(L"--")) {
|
if (!positional_only && arg.starts_with(LSR_STR("--"))) {
|
||||||
auto pos = arg.find(L'=');
|
auto pos = arg.find(L'=');
|
||||||
if (pos != std::wstring::npos) {
|
if (pos != lsr::string::npos) {
|
||||||
named_args[arg.substr(2, pos - 2)] = arg.substr(pos + 1);
|
named_args[arg.substr(2, pos - 2)] = arg.substr(pos + 1);
|
||||||
} else if (++i < argc) {
|
} else if (++i < argc) {
|
||||||
named_args[arg.substr(2)] = argv[i];
|
named_args[arg.substr(2)] = argv[i];
|
||||||
} else {
|
} else {
|
||||||
named_args[arg.substr(2)] = L"";
|
named_args[arg.substr(2)] = LSR_STR("");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
positional_args.push_back(arg);
|
positional_args.push_back(arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
LocalFree(argv);
|
LocalFree(argv);
|
||||||
|
#endif
|
||||||
|
|
||||||
positional_args_ = positional_args;
|
positional_args_ = positional_args;
|
||||||
named_args_ = named_args;
|
named_args_ = named_args;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path CliParser::get_infra_dll() const {
|
|
||||||
std::filesystem::path infra_dll_path{get_with_default(L"infra-dll", L"infra.dll")};
|
|
||||||
return absolute(infra_dll_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::filesystem::path CliParser::get_db_path() const {
|
std::filesystem::path CliParser::get_db_path() const {
|
||||||
std::filesystem::path kugou_db{};
|
std::filesystem::path kugou_db{};
|
||||||
if (const auto& it = named_args_.find(L"db"); it != named_args_.end()) {
|
|
||||||
|
if (const auto& it = named_args_.find(LSR_STR("db")); it != named_args_.end()) {
|
||||||
kugou_db = std::filesystem::path{it->second};
|
kugou_db = std::filesystem::path{it->second};
|
||||||
} else {
|
} else {
|
||||||
|
#ifdef _WIN32
|
||||||
PWSTR pAppDirPath{};
|
PWSTR pAppDirPath{};
|
||||||
SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &pAppDirPath);
|
SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &pAppDirPath);
|
||||||
kugou_db = std::filesystem::path{pAppDirPath} / L"Kugou8" / L"KGMusicV3.db";
|
kugou_db = std::filesystem::path{pAppDirPath} / L"Kugou8" / L"KGMusicV3.db";
|
||||||
CoTaskMemFree(pAppDirPath);
|
CoTaskMemFree(pAppDirPath);
|
||||||
|
#else
|
||||||
|
kugou_db = std::filesystem::path{"KGMusicV3.db"};
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return absolute(kugou_db);
|
return absolute(kugou_db);
|
||||||
|
@ -1,30 +1,34 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <str_helper.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
typedef std::pair<std::vector<std::wstring>, std::unordered_map<std::wstring, std::wstring>> parsed_raw_args_t;
|
typedef std::pair<std::vector<lsr::string>, std::unordered_map<lsr::string, lsr::string>> parsed_raw_args_t;
|
||||||
|
|
||||||
class CliParser {
|
class CliParser {
|
||||||
public:
|
public:
|
||||||
CliParser();
|
CliParser();
|
||||||
void parse_from_cli();
|
void parse_from_cli(int argc, char** argv);
|
||||||
|
|
||||||
[[nodiscard]] std::filesystem::path get_infra_dll() const;
|
[[nodiscard]] std::filesystem::path get_infra_dll() const;
|
||||||
[[nodiscard]] std::filesystem::path get_db_path() const;
|
[[nodiscard]] std::filesystem::path get_db_path() const;
|
||||||
[[nodiscard]] std::wstring get_file_suffix() const { return get_with_default(L"suffix", L"_kgg-dec"); }
|
[[nodiscard]] lsr::string get_file_suffix() const {
|
||||||
[[nodiscard]] bool get_scan_all_file_ext() const { return get_with_default(L"scan-all-file-ext", L"0") == L"1"; };
|
return get_with_default(LSR_STR("suffix"), LSR_STR("_kgg-dec"));
|
||||||
[[nodiscard]] std::vector<std::wstring> get_input_files() const { return positional_args_; }
|
}
|
||||||
|
[[nodiscard]] bool get_scan_all_file_ext() const {
|
||||||
|
return get_with_default(LSR_STR("scan-all-file-ext"), LSR_STR("0")) == LSR_STR("1");
|
||||||
|
};
|
||||||
|
[[nodiscard]] std::vector<lsr::string> get_input_files() const { return positional_args_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::wstring> positional_args_{};
|
std::vector<lsr::string> positional_args_{};
|
||||||
std::unordered_map<std::wstring, std::wstring> named_args_{};
|
std::unordered_map<lsr::string, lsr::string> named_args_{};
|
||||||
|
|
||||||
static parsed_raw_args_t parse();
|
static parsed_raw_args_t parse();
|
||||||
[[nodiscard]] std::wstring get_with_default(const std::wstring& key, const std::wstring& default_value) const {
|
[[nodiscard]] lsr::string get_with_default(const lsr::string& key, const lsr::string& default_value) const {
|
||||||
if (const auto& it = named_args_.find(key); it != named_args_.end()) {
|
if (const auto& it = named_args_.find(key); it != named_args_.end()) {
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
35
third-party/aes/CMakeLists.txt
vendored
Normal file
35
third-party/aes/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
|
project(libaes VERSION 0.0.1 LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
set(SOURCES)
|
||||||
|
if (USE_WIN_CRYPTO)
|
||||||
|
message("Using Windows Crypto API for AES-CBC-128")
|
||||||
|
list(APPEND SOURCES aes_win32.cpp)
|
||||||
|
elseif (USE_OPENSSL)
|
||||||
|
message("Using OpenSSL API for AES-CBC-128")
|
||||||
|
find_package(OpenSSL REQUIRED)
|
||||||
|
list(APPEND SOURCES aes_openssl.cpp)
|
||||||
|
else ()
|
||||||
|
# Tiny AES in C (https://github.com/kokke/tiny-AES-c/)
|
||||||
|
# is licensed under the Unlicense license.
|
||||||
|
message("Using included AES-CBC-128 implementation")
|
||||||
|
list(APPEND SOURCES aes.cpp)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
add_library(libaes STATIC ${SOURCES})
|
||||||
|
target_include_directories(libaes
|
||||||
|
PUBLIC
|
||||||
|
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
|
||||||
|
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||||
|
)
|
||||||
|
if (USE_WIN_CRYPTO)
|
||||||
|
target_link_libraries(libaes PRIVATE bcrypt)
|
||||||
|
target_compile_definitions(libaes PRIVATE USE_WIN_CRYPTO=1)
|
||||||
|
elseif (USE_OPENSSL)
|
||||||
|
target_link_libraries(libaes PRIVATE OpenSSL::Crypto)
|
||||||
|
target_compile_definitions(libaes PRIVATE USE_OPENSSL=1)
|
||||||
|
endif ()
|
360
third-party/aes/aes.cpp
vendored
Normal file
360
third-party/aes/aes.cpp
vendored
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
#include "aes.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#define Nb 4
|
||||||
|
#define Nk 4 // The number of 32 bit words in a key.
|
||||||
|
#define Nr 10 // The number of rounds in AES Cipher.
|
||||||
|
|
||||||
|
namespace AES {
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* Private variables: */
|
||||||
|
/*****************************************************************************/
|
||||||
|
// state - array holding the intermediate results during decryption.
|
||||||
|
typedef uint8_t state_t[4][4];
|
||||||
|
|
||||||
|
// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
|
||||||
|
// The numbers below can be computed dynamically trading ROM for RAM -
|
||||||
|
// This can be useful in (embedded) bootloader applications, where ROM is often limited.
|
||||||
|
static const uint8_t sbox[256] = {
|
||||||
|
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||||
|
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9,
|
||||||
|
0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f,
|
||||||
|
0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07,
|
||||||
|
0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3,
|
||||||
|
0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58,
|
||||||
|
0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3,
|
||||||
|
0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f,
|
||||||
|
0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
|
||||||
|
0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac,
|
||||||
|
0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a,
|
||||||
|
0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70,
|
||||||
|
0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11,
|
||||||
|
0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42,
|
||||||
|
0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16};
|
||||||
|
|
||||||
|
static const uint8_t rsbox[256] = {
|
||||||
|
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39,
|
||||||
|
0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2,
|
||||||
|
0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76,
|
||||||
|
0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc,
|
||||||
|
0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d,
|
||||||
|
0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c,
|
||||||
|
0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f,
|
||||||
|
0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
|
||||||
|
0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62,
|
||||||
|
0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd,
|
||||||
|
0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60,
|
||||||
|
0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d,
|
||||||
|
0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6,
|
||||||
|
0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d};
|
||||||
|
|
||||||
|
// The round constant word array, Rcon[i], contains the values given by
|
||||||
|
// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
|
||||||
|
static const uint8_t Rcon[11] = {0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12),
|
||||||
|
* that you can remove most of the elements in the Rcon array, because they are unused.
|
||||||
|
*
|
||||||
|
* From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
|
||||||
|
*
|
||||||
|
* "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed),
|
||||||
|
* up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
|
||||||
|
*/
|
||||||
|
|
||||||
|
inline uint8_t getSBoxValue(const uint8_t num) {
|
||||||
|
return sbox[num];
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states.
|
||||||
|
void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key) {
|
||||||
|
unsigned i, k;
|
||||||
|
uint8_t temp_arr[4]; // Used for the column/row operations
|
||||||
|
|
||||||
|
// The first round key is the key itself.
|
||||||
|
for (i = 0; i < Nk; ++i) {
|
||||||
|
RoundKey[i * 4 + 0] = Key[i * 4 + 0];
|
||||||
|
RoundKey[i * 4 + 1] = Key[i * 4 + 1];
|
||||||
|
RoundKey[i * 4 + 2] = Key[i * 4 + 2];
|
||||||
|
RoundKey[i * 4 + 3] = Key[i * 4 + 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
// All other "round keys" are found from the previous round keys.
|
||||||
|
for (i = Nk; i < Nb * (Nr + 1); ++i) {
|
||||||
|
{
|
||||||
|
k = (i - 1) * 4;
|
||||||
|
temp_arr[0] = RoundKey[k + 0];
|
||||||
|
temp_arr[1] = RoundKey[k + 1];
|
||||||
|
temp_arr[2] = RoundKey[k + 2];
|
||||||
|
temp_arr[3] = RoundKey[k + 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i % Nk == 0) {
|
||||||
|
// This function shifts the 4 bytes in a word to the left once.
|
||||||
|
// [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
|
||||||
|
|
||||||
|
// Function RotWord()
|
||||||
|
{
|
||||||
|
const uint8_t u8tmp = temp_arr[0];
|
||||||
|
temp_arr[0] = temp_arr[1];
|
||||||
|
temp_arr[1] = temp_arr[2];
|
||||||
|
temp_arr[2] = temp_arr[3];
|
||||||
|
temp_arr[3] = u8tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubWord() is a function that takes a four-byte input word and
|
||||||
|
// applies the S-box to each of the four bytes to produce an output word.
|
||||||
|
|
||||||
|
// Function SubWord()
|
||||||
|
{
|
||||||
|
temp_arr[0] = getSBoxValue(temp_arr[0]);
|
||||||
|
temp_arr[1] = getSBoxValue(temp_arr[1]);
|
||||||
|
temp_arr[2] = getSBoxValue(temp_arr[2]);
|
||||||
|
temp_arr[3] = getSBoxValue(temp_arr[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_arr[0] = temp_arr[0] ^ Rcon[i / Nk];
|
||||||
|
}
|
||||||
|
|
||||||
|
// AES256 code was here.
|
||||||
|
|
||||||
|
const unsigned j = i * 4;
|
||||||
|
k = (i - Nk) * 4;
|
||||||
|
RoundKey[j + 0] = RoundKey[k + 0] ^ temp_arr[0];
|
||||||
|
RoundKey[j + 1] = RoundKey[k + 1] ^ temp_arr[1];
|
||||||
|
RoundKey[j + 2] = RoundKey[k + 2] ^ temp_arr[2];
|
||||||
|
RoundKey[j + 3] = RoundKey[k + 3] ^ temp_arr[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AES_init_ctx_iv(AES_ctx* ctx, const uint8_t* key, const uint8_t* iv) {
|
||||||
|
KeyExpansion(ctx->RoundKey, key);
|
||||||
|
memcpy(ctx->Iv, iv, kBlockLen);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function adds the round key to state.
|
||||||
|
// The round key is added to the state by an XOR function.
|
||||||
|
void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey) {
|
||||||
|
for (uint8_t i = 0; i < 4; ++i) {
|
||||||
|
for (uint8_t j = 0; j < 4; ++j) {
|
||||||
|
(*state)[i][j] ^= RoundKey[round * Nb * 4 + i * Nb + j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The SubBytes Function Substitutes the values in the
|
||||||
|
// state matrix with values in an S-box.
|
||||||
|
void SubBytes(state_t* state) {
|
||||||
|
for (uint8_t i = 0; i < 4; ++i) {
|
||||||
|
for (uint8_t j = 0; j < 4; ++j) {
|
||||||
|
(*state)[j][i] = getSBoxValue((*state)[j][i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ShiftRows() function shifts the rows in the state to the left.
|
||||||
|
// Each row is shifted with different offset.
|
||||||
|
// Offset = Row number. So the first row is not shifted.
|
||||||
|
void ShiftRows(state_t* state) {
|
||||||
|
// Rotate first row 1 column to left
|
||||||
|
uint8_t temp = (*state)[0][1];
|
||||||
|
(*state)[0][1] = (*state)[1][1];
|
||||||
|
(*state)[1][1] = (*state)[2][1];
|
||||||
|
(*state)[2][1] = (*state)[3][1];
|
||||||
|
(*state)[3][1] = temp;
|
||||||
|
|
||||||
|
// Rotate second row 2 columns to left
|
||||||
|
temp = (*state)[0][2];
|
||||||
|
(*state)[0][2] = (*state)[2][2];
|
||||||
|
(*state)[2][2] = temp;
|
||||||
|
|
||||||
|
temp = (*state)[1][2];
|
||||||
|
(*state)[1][2] = (*state)[3][2];
|
||||||
|
(*state)[3][2] = temp;
|
||||||
|
|
||||||
|
// Rotate third row 3 columns to left
|
||||||
|
temp = (*state)[0][3];
|
||||||
|
(*state)[0][3] = (*state)[3][3];
|
||||||
|
(*state)[3][3] = (*state)[2][3];
|
||||||
|
(*state)[2][3] = (*state)[1][3];
|
||||||
|
(*state)[1][3] = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint8_t xtime(uint8_t x) {
|
||||||
|
return x << 1 ^ (x >> 7 & 1) * 0x1b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MixColumns function mixes the columns of the state matrix
|
||||||
|
void MixColumns(state_t* state) {
|
||||||
|
for (uint8_t i = 0; i < 4; ++i) {
|
||||||
|
uint8_t t = (*state)[i][0];
|
||||||
|
uint8_t Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3];
|
||||||
|
uint8_t Tm = (*state)[i][0] ^ (*state)[i][1];
|
||||||
|
Tm = xtime(Tm);
|
||||||
|
(*state)[i][0] ^= Tm ^ Tmp;
|
||||||
|
Tm = (*state)[i][1] ^ (*state)[i][2];
|
||||||
|
Tm = xtime(Tm);
|
||||||
|
(*state)[i][1] ^= Tm ^ Tmp;
|
||||||
|
Tm = (*state)[i][2] ^ (*state)[i][3];
|
||||||
|
Tm = xtime(Tm);
|
||||||
|
(*state)[i][2] ^= Tm ^ Tmp;
|
||||||
|
Tm = (*state)[i][3] ^ t;
|
||||||
|
Tm = xtime(Tm);
|
||||||
|
(*state)[i][3] ^= Tm ^ Tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiply is used to multiply numbers in the field GF(2^8)
|
||||||
|
// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
|
||||||
|
// The compiler seems to be able to vectorize the operation better this way.
|
||||||
|
// See https://github.com/kokke/tiny-AES-c/pull/34
|
||||||
|
#if MULTIPLY_AS_A_FUNCTION
|
||||||
|
static uint8_t Multiply(uint8_t x, uint8_t y) {
|
||||||
|
return (((y & 1) * x) ^ ((y >> 1 & 1) * xtime(x)) ^ ((y >> 2 & 1) * xtime(xtime(x))) ^
|
||||||
|
((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^
|
||||||
|
((y >> 4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define Multiply(x, y) \
|
||||||
|
(((y & 1) * x) ^ ((y >> 1 & 1) * xtime(x)) ^ ((y >> 2 & 1) * xtime(xtime(x))) ^ \
|
||||||
|
((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^ ((y >> 4 & 1) * xtime(xtime(xtime(xtime(x))))))
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline uint8_t getSBoxInvert(uint8_t num) {
|
||||||
|
return rsbox[num];
|
||||||
|
}
|
||||||
|
|
||||||
|
// MixColumns function mixes the columns of the state matrix.
|
||||||
|
// The method used to multiply may be difficult to understand for the inexperienced.
|
||||||
|
// Please use the references to gain more information.
|
||||||
|
void InvMixColumns(state_t* state) {
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
uint8_t a = (*state)[i][0];
|
||||||
|
uint8_t b = (*state)[i][1];
|
||||||
|
uint8_t c = (*state)[i][2];
|
||||||
|
uint8_t d = (*state)[i][3];
|
||||||
|
|
||||||
|
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
|
||||||
|
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
|
||||||
|
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
|
||||||
|
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The SubBytes Function Substitutes the values in the
|
||||||
|
// state matrix with values in an S-box.
|
||||||
|
void InvSubBytes(state_t* state) {
|
||||||
|
for (uint8_t i = 0; i < 4; ++i) {
|
||||||
|
for (uint8_t j = 0; j < 4; ++j) {
|
||||||
|
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InvShiftRows(state_t* state) {
|
||||||
|
// Rotate first row 1 column to right
|
||||||
|
uint8_t temp = (*state)[3][1];
|
||||||
|
(*state)[3][1] = (*state)[2][1];
|
||||||
|
(*state)[2][1] = (*state)[1][1];
|
||||||
|
(*state)[1][1] = (*state)[0][1];
|
||||||
|
(*state)[0][1] = temp;
|
||||||
|
|
||||||
|
// Rotate second row 2 columns to right
|
||||||
|
temp = (*state)[0][2];
|
||||||
|
(*state)[0][2] = (*state)[2][2];
|
||||||
|
(*state)[2][2] = temp;
|
||||||
|
|
||||||
|
temp = (*state)[1][2];
|
||||||
|
(*state)[1][2] = (*state)[3][2];
|
||||||
|
(*state)[3][2] = temp;
|
||||||
|
|
||||||
|
// Rotate third row 3 columns to right
|
||||||
|
temp = (*state)[0][3];
|
||||||
|
(*state)[0][3] = (*state)[1][3];
|
||||||
|
(*state)[1][3] = (*state)[2][3];
|
||||||
|
(*state)[2][3] = (*state)[3][3];
|
||||||
|
(*state)[3][3] = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cipher is the main function that encrypts the PlainText.
|
||||||
|
void Cipher(state_t* state, const uint8_t* RoundKey) {
|
||||||
|
uint8_t round = 0;
|
||||||
|
|
||||||
|
// Add the First round key to the state before starting the rounds.
|
||||||
|
AddRoundKey(0, state, RoundKey);
|
||||||
|
|
||||||
|
// There will be Nr rounds.
|
||||||
|
// The first Nr-1 rounds are identical.
|
||||||
|
// These Nr rounds are executed in the loop below.
|
||||||
|
// Last one without MixColumns()
|
||||||
|
for (round = 1;; ++round) {
|
||||||
|
SubBytes(state);
|
||||||
|
ShiftRows(state);
|
||||||
|
if (round == Nr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
MixColumns(state);
|
||||||
|
AddRoundKey(round, state, RoundKey);
|
||||||
|
}
|
||||||
|
// Add round key to last round
|
||||||
|
AddRoundKey(Nr, state, RoundKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InvCipher(state_t* state, const uint8_t* RoundKey) {
|
||||||
|
uint8_t round = 0;
|
||||||
|
|
||||||
|
// Add the First round key to the state before starting the rounds.
|
||||||
|
AddRoundKey(Nr, state, RoundKey);
|
||||||
|
|
||||||
|
// There will be Nr rounds.
|
||||||
|
// The first Nr-1 rounds are identical.
|
||||||
|
// These Nr rounds are executed in the loop below.
|
||||||
|
// Last one without InvMixColumn()
|
||||||
|
for (round = Nr - 1;; --round) {
|
||||||
|
InvShiftRows(state);
|
||||||
|
InvSubBytes(state);
|
||||||
|
AddRoundKey(round, state, RoundKey);
|
||||||
|
if (round == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
InvMixColumns(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void XorWithIv(uint8_t* buf, const uint8_t* Iv) {
|
||||||
|
for (uint8_t i = 0; i < kBlockLen; ++i) // The block in AES is always 128bit no matter the key size
|
||||||
|
{
|
||||||
|
buf[i] ^= Iv[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AES_CBC_encrypt_buffer(AES_ctx* ctx, uint8_t* buf, size_t length) {
|
||||||
|
uint8_t* Iv = ctx->Iv;
|
||||||
|
for (size_t i = 0; i < length; i += kBlockLen) {
|
||||||
|
XorWithIv(buf, Iv);
|
||||||
|
Cipher(reinterpret_cast<state_t*>(buf), ctx->RoundKey);
|
||||||
|
Iv = buf;
|
||||||
|
buf += kBlockLen;
|
||||||
|
}
|
||||||
|
/* store Iv in ctx for next call */
|
||||||
|
memcpy(ctx->Iv, Iv, kBlockLen);
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AES_CBC_decrypt_buffer(AES_ctx* ctx, uint8_t* buf, size_t length) {
|
||||||
|
for (size_t i = 0; i < length; i += kBlockLen) {
|
||||||
|
uint8_t storeNextIv[kBlockLen];
|
||||||
|
memcpy(storeNextIv, buf, kBlockLen);
|
||||||
|
InvCipher(reinterpret_cast<state_t*>(buf), ctx->RoundKey);
|
||||||
|
XorWithIv(buf, ctx->Iv);
|
||||||
|
memcpy(ctx->Iv, storeNextIv, kBlockLen);
|
||||||
|
buf += kBlockLen;
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace AES
|
50
third-party/aes/aes.h
vendored
Normal file
50
third-party/aes/aes.h
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#if USE_WIN_CRYPTO
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <bcrypt.h>
|
||||||
|
#elif USE_OPENSSL
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace AES {
|
||||||
|
|
||||||
|
constexpr size_t kKeyLen = 16; // Key length in bytes
|
||||||
|
constexpr size_t kKeyExpansionSize = 176;
|
||||||
|
constexpr size_t kBlockLen = 16; // Block length in bytes - AES is 128b block only
|
||||||
|
|
||||||
|
struct AES_ctx {
|
||||||
|
#if USE_WIN_CRYPTO
|
||||||
|
BCRYPT_ALG_HANDLE hAlg;
|
||||||
|
BCRYPT_KEY_HANDLE hKey;
|
||||||
|
uint8_t iv[0x10];
|
||||||
|
#elif USE_OPENSSL
|
||||||
|
EVP_CIPHER_CTX* cipher_ctx;
|
||||||
|
#else
|
||||||
|
uint8_t RoundKey[kKeyExpansionSize];
|
||||||
|
uint8_t Iv[16];
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
bool AES_init_ctx_iv(AES_ctx* ctx, const uint8_t* key, const uint8_t* iv);
|
||||||
|
|
||||||
|
// buffer size MUST be mutile of AES_BLOCKLEN;
|
||||||
|
// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
|
||||||
|
// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv()
|
||||||
|
// no IV should ever be reused with the same key
|
||||||
|
size_t AES_CBC_encrypt_buffer(AES_ctx* ctx, uint8_t* buf, size_t length);
|
||||||
|
size_t AES_CBC_decrypt_buffer(AES_ctx* ctx, uint8_t* buf, size_t length);
|
||||||
|
|
||||||
|
#if USE_WIN_CRYPTO || USE_OPENSSL
|
||||||
|
bool AES_cleanup(AES_ctx* ctx);
|
||||||
|
#else
|
||||||
|
inline bool AES_cleanup(AES_ctx* ctx) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace AES
|
43
third-party/aes/aes_openssl.cpp
vendored
Normal file
43
third-party/aes/aes_openssl.cpp
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "aes.h"
|
||||||
|
|
||||||
|
namespace AES {
|
||||||
|
|
||||||
|
bool AES_init_ctx_iv(AES_ctx* ctx, const uint8_t* key, const uint8_t* iv) {
|
||||||
|
ctx->cipher_ctx = EVP_CIPHER_CTX_new();
|
||||||
|
if (!ctx->cipher_ctx) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EVP_DecryptInit_ex(ctx->cipher_ctx, EVP_aes_128_cbc(), nullptr, key, iv) != 1) {
|
||||||
|
AES_cleanup(ctx);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
EVP_CIPHER_CTX_set_padding(ctx->cipher_ctx, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length) {
|
||||||
|
// not implemented
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length) {
|
||||||
|
auto buf_len = static_cast<int>(length);
|
||||||
|
EVP_DecryptUpdate(ctx->cipher_ctx, buf, &buf_len, buf, buf_len);
|
||||||
|
assert(buf_len == length && "AES_CBC_decrypt_buffer: buffer length mismatch");
|
||||||
|
return buf_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AES_cleanup(AES_ctx* ctx) {
|
||||||
|
if (ctx->cipher_ctx) {
|
||||||
|
EVP_CIPHER_CTX_free(ctx->cipher_ctx);
|
||||||
|
ctx->cipher_ctx = nullptr;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace AES
|
59
third-party/aes/aes_win32.cpp
vendored
Normal file
59
third-party/aes/aes_win32.cpp
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "aes.h"
|
||||||
|
|
||||||
|
namespace AES {
|
||||||
|
// ReSharper disable CppCStyleCast
|
||||||
|
|
||||||
|
bool AES_init_ctx_iv(AES_ctx* ctx, const uint8_t* key, const uint8_t* iv) {
|
||||||
|
memset(ctx, 0, sizeof(AES_ctx));
|
||||||
|
|
||||||
|
if (BCryptOpenAlgorithmProvider(&ctx->hAlg, BCRYPT_AES_ALGORITHM, nullptr, 0) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BCryptSetProperty(ctx->hAlg, BCRYPT_CHAINING_MODE, (PUCHAR)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC),
|
||||||
|
0) != 0) {
|
||||||
|
BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BCryptGenerateSymmetricKey(ctx->hAlg, &ctx->hKey, nullptr, 0, (PUCHAR)key, 0x10, 0) != 0) {
|
||||||
|
BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::copy_n(iv, 0x10, ctx->iv);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// buffer size MUST be mutile of AES_BLOCKLEN;
|
||||||
|
// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
|
||||||
|
// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv()
|
||||||
|
// no IV should ever be reused with the same key
|
||||||
|
size_t AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length) {
|
||||||
|
// not implemented
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length) {
|
||||||
|
ULONG cbData{0};
|
||||||
|
const auto len = static_cast<ULONG>(length);
|
||||||
|
if (BCryptDecrypt(ctx->hKey, buf, len, nullptr, ctx->iv, sizeof(ctx->iv), buf, len, &cbData, 0) != 0) {
|
||||||
|
assert(false && "BCryptDecrypt failed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(cbData == len && "AES_CBC_decrypt_buffer: cbData != length");
|
||||||
|
return cbData;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AES_cleanup(AES_ctx* ctx) {
|
||||||
|
BCryptDestroyKey(ctx->hKey);
|
||||||
|
BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
|
||||||
|
memset(ctx, 0, sizeof(AES_ctx));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace AES
|
36
third-party/md5/CMakeLists.txt
vendored
Normal file
36
third-party/md5/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
|
project(md5 VERSION 0.0.1 LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
set(SOURCES)
|
||||||
|
if (USE_WIN_CRYPTO)
|
||||||
|
message("Using Windows Crypto API for MD5")
|
||||||
|
list(APPEND SOURCES md5_win32.cpp)
|
||||||
|
elseif (USE_OPENSSL)
|
||||||
|
message("Using OpenSSL API for MD5")
|
||||||
|
find_package(OpenSSL REQUIRED)
|
||||||
|
list(APPEND SOURCES md5_openssl.cpp)
|
||||||
|
else ()
|
||||||
|
# Derived from the "RSA Data Security, Inc. MD5 Message-Digest Algorithm":
|
||||||
|
# https://github.com/freebsd/freebsd-src/blob/release/14.2.0/sys/kern/md5c.c
|
||||||
|
message("Using included MD5 implementation")
|
||||||
|
list(APPEND SOURCES md5.cpp)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
add_library(libmd5 STATIC ${SOURCES})
|
||||||
|
target_include_directories(libmd5
|
||||||
|
PUBLIC
|
||||||
|
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
|
||||||
|
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (USE_WIN_CRYPTO)
|
||||||
|
target_link_libraries(libmd5 PRIVATE crypt32)
|
||||||
|
target_compile_definitions(libmd5 PRIVATE USE_WIN_CRYPTO=1)
|
||||||
|
elseif (USE_OPENSSL)
|
||||||
|
target_link_libraries(libmd5 PRIVATE OpenSSL::Crypto)
|
||||||
|
target_compile_definitions(libmd5 PRIVATE USE_OPENSSL=1)
|
||||||
|
endif ()
|
284
third-party/md5/md5.cpp
vendored
Normal file
284
third-party/md5/md5.cpp
vendored
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
// Derived from the "RSA Data Security, Inc. MD5 Message-Digest Algorithm":
|
||||||
|
// src: https://github.com/freebsd/freebsd-src/blob/release/14.2.0/sys/kern/md5c.c
|
||||||
|
|
||||||
|
#include <bit>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "md5.h"
|
||||||
|
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#define bswap_u16 _byteswap_ushort
|
||||||
|
#define bswap_u32 _byteswap_ulong
|
||||||
|
#define bswap_u64 _byteswap_uint64
|
||||||
|
#else
|
||||||
|
#define bswap_u16 __builtin_bswap16
|
||||||
|
#define bswap_u32 __builtin_bswap32
|
||||||
|
#define bswap_u64 __builtin_bswap64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Encode(uint8_t* output, const T input)
|
||||||
|
requires(std::is_integral_v<T> && (sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8))
|
||||||
|
{
|
||||||
|
if constexpr (std::endian::native == std::endian::little || sizeof(T) == 1) {
|
||||||
|
memcpy(output, &input, sizeof(T));
|
||||||
|
// ReSharper disable once CppDFAUnreachableCode
|
||||||
|
} else if constexpr (sizeof(T) == 2) {
|
||||||
|
*reinterpret_cast<uint16_t*>(output) = bswap_u16(input);
|
||||||
|
} else if constexpr (sizeof(T) == 4) {
|
||||||
|
*reinterpret_cast<uint32_t*>(output) = bswap_u32(input);
|
||||||
|
} else if constexpr (sizeof(T) == 8) {
|
||||||
|
*reinterpret_cast<uint64_t*>(output) = bswap_u64(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Decode(T* output, const uint8_t* input)
|
||||||
|
requires(std::is_integral_v<T> && (sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8))
|
||||||
|
{
|
||||||
|
if constexpr (std::endian::native == std::endian::little) {
|
||||||
|
memcpy(output, input, sizeof(T));
|
||||||
|
// ReSharper disable once CppDFAUnreachableCode
|
||||||
|
} else if constexpr (sizeof(T) == 2) {
|
||||||
|
*output = bswap_u16(*reinterpret_cast<const uint16_t*>(input));
|
||||||
|
} else if constexpr (sizeof(T) == 4) {
|
||||||
|
*output = bswap_u32(*reinterpret_cast<const uint32_t*>(input));
|
||||||
|
} else if constexpr (sizeof(T) == 8) {
|
||||||
|
*output = bswap_u64(*reinterpret_cast<const uint64_t*>(input));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Encode(unsigned char* output, const uint32_t* input, const unsigned int len) {
|
||||||
|
if constexpr (std::endian::native == std::endian::little) {
|
||||||
|
memcpy(output, input, len);
|
||||||
|
} else {
|
||||||
|
// ReSharper disable once CppDFAUnreachableCode
|
||||||
|
for (unsigned int i = 0; i < len; i += 4, output += 4) {
|
||||||
|
Encode(output, input[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Decode(uint32_t* output, const unsigned char* input, const unsigned int len) {
|
||||||
|
// ReSharper disable once CppDFAUnreachableCode
|
||||||
|
if constexpr (std::endian::native == std::endian::little) {
|
||||||
|
memcpy(output, input, len);
|
||||||
|
} else {
|
||||||
|
for (unsigned int i = 0; i < len; i += 4, ++output) {
|
||||||
|
Decode(output, &input[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MD5Transform(uint32_t state[4], const unsigned char block[64]);
|
||||||
|
|
||||||
|
static unsigned char PADDING[64] = {0x80};
|
||||||
|
|
||||||
|
/* F, G, H and I are basic MD5 functions. */
|
||||||
|
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
|
||||||
|
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
|
||||||
|
#define H(x, y, z) ((x) ^ (y) ^ (z))
|
||||||
|
#define I(x, y, z) ((y) ^ ((x) | (~z)))
|
||||||
|
|
||||||
|
/* ROTATE_LEFT rotates x left n bits. */
|
||||||
|
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
|
||||||
|
* Rotation is separate from addition to prevent recomputation.
|
||||||
|
*/
|
||||||
|
#define FF(a, b, c, d, x, s, ac) \
|
||||||
|
{ \
|
||||||
|
(a) += F((b), (c), (d)) + (x) + (uint32_t)(ac); \
|
||||||
|
(a) = ROTATE_LEFT((a), (s)); \
|
||||||
|
(a) += (b); \
|
||||||
|
}
|
||||||
|
#define GG(a, b, c, d, x, s, ac) \
|
||||||
|
{ \
|
||||||
|
(a) += G((b), (c), (d)) + (x) + (uint32_t)(ac); \
|
||||||
|
(a) = ROTATE_LEFT((a), (s)); \
|
||||||
|
(a) += (b); \
|
||||||
|
}
|
||||||
|
#define HH(a, b, c, d, x, s, ac) \
|
||||||
|
{ \
|
||||||
|
(a) += H((b), (c), (d)) + (x) + (uint32_t)(ac); \
|
||||||
|
(a) = ROTATE_LEFT((a), (s)); \
|
||||||
|
(a) += (b); \
|
||||||
|
}
|
||||||
|
#define II(a, b, c, d, x, s, ac) \
|
||||||
|
{ \
|
||||||
|
(a) += I((b), (c), (d)) + (x) + (uint32_t)(ac); \
|
||||||
|
(a) = ROTATE_LEFT((a), (s)); \
|
||||||
|
(a) += (b); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MD5 block update operation. Continues an MD5 message-digest
|
||||||
|
* operation, processing another message block, and updating the
|
||||||
|
* context.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void md5_update(MD5_CTX* ctx, const uint8_t* in, const size_t len) {
|
||||||
|
unsigned int i{0};
|
||||||
|
const unsigned char* input = in;
|
||||||
|
|
||||||
|
/* Compute number of bytes mod 64 */
|
||||||
|
unsigned int index = ctx->count % 64;
|
||||||
|
ctx->count += len;
|
||||||
|
|
||||||
|
// ReSharper disable once CppTooWideScopeInitStatement
|
||||||
|
const unsigned int partLen = 64 - index;
|
||||||
|
|
||||||
|
/* Transform as many times as possible. */
|
||||||
|
if (len >= partLen) {
|
||||||
|
memcpy(&ctx->buffer[index], input, partLen);
|
||||||
|
MD5Transform(ctx->state, ctx->buffer);
|
||||||
|
|
||||||
|
for (i = partLen; i + 63 < len; i += 64) {
|
||||||
|
MD5Transform(ctx->state, &input[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buffer remaining input */
|
||||||
|
memcpy(&ctx->buffer[index], &input[i], len - i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MD5 padding. Adds padding followed by original length.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void MD5Pad(MD5_CTX* context) {
|
||||||
|
unsigned char bits[8];
|
||||||
|
|
||||||
|
/* Save number of bits */
|
||||||
|
Encode(bits, context->count << 3);
|
||||||
|
|
||||||
|
/* Pad out to 56 mod 64. */
|
||||||
|
const unsigned int index = context->count % 64;
|
||||||
|
const unsigned int padLen = index < 56 ? 56 - index : 120 - index;
|
||||||
|
md5_update(context, PADDING, padLen);
|
||||||
|
|
||||||
|
/* Append length (before padding) */
|
||||||
|
md5_update(context, bits, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MD5 finalization. Ends an MD5 message-digest operation, writing
|
||||||
|
* the message digest and zeroizing the context.
|
||||||
|
*/
|
||||||
|
void md5_final(MD5_CTX* ctx, uint8_t* digest) {
|
||||||
|
/* Do padding. */
|
||||||
|
MD5Pad(ctx);
|
||||||
|
|
||||||
|
/* Store state in digest */
|
||||||
|
Encode(digest, ctx->state, MD5_DIGEST_LENGTH);
|
||||||
|
|
||||||
|
/* Zeroize sensitive information. */
|
||||||
|
memset(ctx, 0, sizeof(*ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MD5 basic transformation. Transforms state based on block. */
|
||||||
|
void MD5Transform(uint32_t state[4], const unsigned char block[64]) {
|
||||||
|
uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16];
|
||||||
|
|
||||||
|
Decode(x, block, 64);
|
||||||
|
|
||||||
|
/* Round 1 */
|
||||||
|
constexpr int S11 = 7;
|
||||||
|
constexpr int S12 = 12;
|
||||||
|
constexpr int S13 = 17;
|
||||||
|
constexpr int S14 = 22;
|
||||||
|
FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */
|
||||||
|
FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */
|
||||||
|
FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */
|
||||||
|
FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */
|
||||||
|
FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */
|
||||||
|
FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */
|
||||||
|
FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */
|
||||||
|
FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */
|
||||||
|
FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */
|
||||||
|
FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */
|
||||||
|
FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
|
||||||
|
FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
|
||||||
|
FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
|
||||||
|
FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
|
||||||
|
FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
|
||||||
|
FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
|
||||||
|
|
||||||
|
/* Round 2 */
|
||||||
|
constexpr int S21 = 5;
|
||||||
|
constexpr int S22 = 9;
|
||||||
|
constexpr int S23 = 14;
|
||||||
|
constexpr int S24 = 20;
|
||||||
|
GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */
|
||||||
|
GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */
|
||||||
|
GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
|
||||||
|
GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */
|
||||||
|
GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */
|
||||||
|
GG(d, a, b, c, x[10], S22, 0x2441453); /* 22 */
|
||||||
|
GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
|
||||||
|
GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */
|
||||||
|
GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */
|
||||||
|
GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
|
||||||
|
GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */
|
||||||
|
GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */
|
||||||
|
GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
|
||||||
|
GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */
|
||||||
|
GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */
|
||||||
|
GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
|
||||||
|
|
||||||
|
/* Round 3 */
|
||||||
|
constexpr int S31 = 4;
|
||||||
|
constexpr int S32 = 11;
|
||||||
|
constexpr int S33 = 16;
|
||||||
|
constexpr int S34 = 23;
|
||||||
|
HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */
|
||||||
|
HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */
|
||||||
|
HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
|
||||||
|
HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
|
||||||
|
HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */
|
||||||
|
HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */
|
||||||
|
HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */
|
||||||
|
HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
|
||||||
|
HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
|
||||||
|
HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */
|
||||||
|
HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */
|
||||||
|
HH(b, c, d, a, x[6], S34, 0x4881d05); /* 44 */
|
||||||
|
HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */
|
||||||
|
HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
|
||||||
|
HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
|
||||||
|
HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */
|
||||||
|
|
||||||
|
/* Round 4 */
|
||||||
|
constexpr int S41 = 6;
|
||||||
|
constexpr int S42 = 10;
|
||||||
|
constexpr int S43 = 15;
|
||||||
|
constexpr int S44 = 21;
|
||||||
|
II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */
|
||||||
|
II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */
|
||||||
|
II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
|
||||||
|
II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */
|
||||||
|
II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
|
||||||
|
II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */
|
||||||
|
II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
|
||||||
|
II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */
|
||||||
|
II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */
|
||||||
|
II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
|
||||||
|
II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */
|
||||||
|
II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
|
||||||
|
II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */
|
||||||
|
II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
|
||||||
|
II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */
|
||||||
|
II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */
|
||||||
|
|
||||||
|
state[0] += a;
|
||||||
|
state[1] += b;
|
||||||
|
state[2] += c;
|
||||||
|
state[3] += d;
|
||||||
|
|
||||||
|
/* Zeroize sensitive information. */
|
||||||
|
memset(x, 0, sizeof(x));
|
||||||
|
}
|
68
third-party/md5/md5.h
vendored
Normal file
68
third-party/md5/md5.h
vendored
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#if USE_WIN_CRYPTO
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <wincrypt.h>
|
||||||
|
#elif USE_OPENSSL
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MD5_DIGEST_LENGTH 16
|
||||||
|
|
||||||
|
struct MD5_CTX {
|
||||||
|
#if USE_WIN_CRYPTO
|
||||||
|
HCRYPTPROV hProv;
|
||||||
|
HCRYPTHASH hHash;
|
||||||
|
#elif USE_OPENSSL
|
||||||
|
EVP_MD_CTX* md_ctx;
|
||||||
|
#else
|
||||||
|
uint64_t count; /* number of bits, modulo 2^64 (lsb first) */
|
||||||
|
uint32_t state[4]; /* state (ABCD) */
|
||||||
|
unsigned char buffer[64]; /* input buffer */
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#if USE_WIN_CRYPTO || USE_OPENSSL
|
||||||
|
bool md5_init(MD5_CTX* ctx);
|
||||||
|
bool md5_cleanup(MD5_CTX* ctx);
|
||||||
|
#else
|
||||||
|
/* MD5 initialization. Begins an MD5 operation, writing a new context. */
|
||||||
|
inline bool md5_init(MD5_CTX* context) {
|
||||||
|
context->count = 0;
|
||||||
|
|
||||||
|
/* Load magic initialization constants. */
|
||||||
|
context->state[0] = 0x67452301;
|
||||||
|
context->state[1] = 0xefcdab89;
|
||||||
|
context->state[2] = 0x98badcfe;
|
||||||
|
context->state[3] = 0x10325476;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool md5_cleanup(MD5_CTX* ctx) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void md5_update(MD5_CTX* ctx, const uint8_t* in, size_t len);
|
||||||
|
void md5_final(MD5_CTX* ctx, uint8_t* digest);
|
||||||
|
|
||||||
|
inline void md5(uint8_t* digest, const uint8_t* in, const size_t len) {
|
||||||
|
MD5_CTX ctx{};
|
||||||
|
md5_init(&ctx);
|
||||||
|
md5_update(&ctx, in, len);
|
||||||
|
md5_final(&ctx, digest);
|
||||||
|
md5_cleanup(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void md5(uint8_t* digest, const uint8_t* in, const size_t len, const uint8_t* in2, size_t len2) {
|
||||||
|
MD5_CTX ctx{};
|
||||||
|
md5_init(&ctx);
|
||||||
|
md5_update(&ctx, in, len);
|
||||||
|
md5_update(&ctx, in2, len2);
|
||||||
|
md5_final(&ctx, digest);
|
||||||
|
md5_cleanup(&ctx);
|
||||||
|
}
|
32
third-party/md5/md5_openssl.cpp
vendored
Normal file
32
third-party/md5/md5_openssl.cpp
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include "md5.h"
|
||||||
|
|
||||||
|
bool md5_init(MD5_CTX* ctx) {
|
||||||
|
memset(ctx, 0, sizeof(*ctx));
|
||||||
|
|
||||||
|
ctx->md_ctx = EVP_MD_CTX_new();
|
||||||
|
if (!ctx->md_ctx) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EVP_DigestInit_ex(ctx->md_ctx, EVP_md5(), nullptr) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool md5_cleanup(MD5_CTX* ctx) {
|
||||||
|
if (ctx->md_ctx != nullptr) {
|
||||||
|
EVP_MD_CTX_free(ctx->md_ctx);
|
||||||
|
}
|
||||||
|
memset(ctx, 0xcc, sizeof(*ctx));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void md5_update(MD5_CTX* ctx, const uint8_t* in, size_t len) {
|
||||||
|
EVP_DigestUpdate(ctx->md_ctx, in, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void md5_final(MD5_CTX* ctx, uint8_t* digest) {
|
||||||
|
unsigned int len{MD5_DIGEST_LENGTH};
|
||||||
|
EVP_DigestFinal_ex(ctx->md_ctx, digest, &len);
|
||||||
|
EVP_MD_CTX_reset(ctx->md_ctx);
|
||||||
|
}
|
35
third-party/md5/md5_win32.cpp
vendored
Normal file
35
third-party/md5/md5_win32.cpp
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#include "md5.h"
|
||||||
|
|
||||||
|
bool md5_init(MD5_CTX* ctx) {
|
||||||
|
if (!CryptAcquireContext(&ctx->hProv, nullptr, nullptr, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the hash object
|
||||||
|
if (!CryptCreateHash(ctx->hProv, CALG_MD5, 0, 0, &ctx->hHash)) {
|
||||||
|
CryptReleaseContext(ctx->hProv, 0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void md5_update(MD5_CTX* ctx, const uint8_t* in, size_t len) {
|
||||||
|
CryptHashData(ctx->hHash, in, static_cast<DWORD>(len), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void md5_final(MD5_CTX* ctx, uint8_t* digest) {
|
||||||
|
DWORD dataLen = MD5_DIGEST_LENGTH;
|
||||||
|
CryptGetHashParam(ctx->hHash, HP_HASHVAL, digest, &dataLen, 0);
|
||||||
|
CryptDestroyHash(ctx->hHash);
|
||||||
|
ctx->hHash = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool md5_cleanup(MD5_CTX* ctx) {
|
||||||
|
if (ctx->hHash) {
|
||||||
|
CryptDestroyHash(ctx->hHash);
|
||||||
|
}
|
||||||
|
CryptReleaseContext(ctx->hProv, 0);
|
||||||
|
memset(ctx, 0, sizeof(MD5_CTX));
|
||||||
|
return true;
|
||||||
|
}
|
2
third-party/sqlite3/.gitignore
vendored
Normal file
2
third-party/sqlite3/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
sqlite-*/
|
||||||
|
sqlite-*.zip
|
11
third-party/sqlite3/CMakeLists.txt
vendored
Normal file
11
third-party/sqlite3/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
|
project(sqlite VERSION 0.0.1 LANGUAGES C)
|
||||||
|
|
||||||
|
# SQLite3 is in the public domain, see https://www.sqlite.org/copyright.html
|
||||||
|
add_library(sqlite3 STATIC sqlite-amalgamation-3470200/sqlite3.c)
|
||||||
|
target_include_directories(sqlite3
|
||||||
|
PUBLIC
|
||||||
|
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/sqlite-amalgamation-3470200>"
|
||||||
|
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||||
|
)
|
11
third-party/sqlite3/fetch_sqlite3.sh
vendored
Normal file
11
third-party/sqlite3/fetch_sqlite3.sh
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/sh -ex
|
||||||
|
|
||||||
|
NAME="sqlite-amalgamation-3470200"
|
||||||
|
|
||||||
|
if ! sha256sum -c sqlite3.sha256sum; then
|
||||||
|
rm -f sqlite3-*.zip
|
||||||
|
curl -fsLO "https://www.sqlite.org/2024/$NAME.zip"
|
||||||
|
sha256sum -c sqlite3.sha256sum || exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
unzip -n "$NAME.zip"
|
1
third-party/sqlite3/sqlite3.sha256sum
vendored
Normal file
1
third-party/sqlite3/sqlite3.sha256sum
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
aa73d8748095808471deaa8e6f34aa700e37f2f787f4425744f53fdd15a89c40 sqlite-amalgamation-3470200.zip
|
Loading…
Reference in New Issue
Block a user