Skip to content

Commit

Permalink
feat: get encryption work
Browse files Browse the repository at this point in the history
  • Loading branch information
pnck committed May 2, 2024
1 parent 8a998a7 commit 643df72
Show file tree
Hide file tree
Showing 22 changed files with 1,065 additions and 114 deletions.
19 changes: 17 additions & 2 deletions .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
AccessModifierOffset: -4

# Align parameters on the open bracket
AlignAfterOpenBracket: Align
AlignAfterOpenBracket: 'Align'

# Disallows contracting simple braced statements to a single line
AllowShortBlocksOnASingleLine: 'false'
Expand Down Expand Up @@ -61,4 +61,19 @@ PenaltyReturnTypeOnItsOwnLine: 10000
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, GIVEN, WHEN, AND_WHEN, THEN, AND_THEN, SECTION ]

SortIncludes: 'false'
#...

# https://stackoverflow.com/questions/73451931/changing-the-location-of-the-line-continuations-used-in-macros-with-clang
AlignEscapedNewlines: Left

# https://clang.llvm.org/docs/ClangFormatStyleOptions.html#breakafterattributes
BreakAfterAttributes: 'Leave'

# https://clang.llvm.org/docs/ClangFormatStyleOptions.html#alignarrayofstructures
AlignArrayOfStructures: 'Left'

# https://clang.llvm.org/docs/ClangFormatStyleOptions.html#binpackarguments
BinPackArguments: 'false'
BinPackParameters: 'false'

# https://clang.llvm.org/docs/ClangFormatStyleOptions.html#alignoperands
AlignOperands: 'AlignAfterOperator'
45 changes: 40 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ on:

pull_request:
branches: ['main']

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

jobs:
test:
test-windows:
name: Test (Windows)
runs-on: windows-latest
strategy:
Expand Down Expand Up @@ -45,18 +45,53 @@ jobs:
- name: Run Tests (x86)
working-directory: ${{ github.workspace }}/${{ matrix.config }}
run: .\foo_input_ncm_tests.exe
run: .\foo_input_ncm_tests.exe --gtest_repeat=10 --gtest_shuffle
if: matrix.arch == 'x86'

- name: Run Tests (x64)
working-directory: ${{ github.workspace }}/${{ matrix.arch }}/${{ matrix.config }}
run: .\foo_input_ncm_tests.exe
run: .\foo_input_ncm_tests.exe --gtest_repeat=10 --gtest_shuffle
if: matrix.arch == 'x64'

test-macos:
name: Test (macOS)
runs-on: macos-14
strategy:
fail-fast: false
matrix:
config: [Release, Debug]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive

- name: Download Foobar2000 SDK
run: |
curl -L https://www.foobar2000.org/files/SDK-2023-09-23.7z -o vendor/foobar2000_sdk.7z
7z x vendor/foobar2000_sdk.7z -ovendor/sdk
ls vendor/sdk
- name: Setup GoogleTest Xcode Project
working-directory: ${{ github.workspace }}/vendor/googletest
run: |
mkdir build
cd build && cmake -G Xcode ..
- name: Build Tests
working-directory: ${{ github.workspace }}
run: |
xcodebuild clean build -workspace foo_input_ncm.xcworkspace -scheme foo_input_ncm_tests -configuration ${{ matrix.config }} -derivedDataPath .
- name: Run Tests
working-directory: ${{ github.workspace }}/Build/Products/${{ matrix.config }}
run: |
./foo_input_ncm_tests --gtest_repeat=10 --gtest_shuffle
matrix-build:
name: Matrix Build
runs-on: ${{ matrix.os }}
needs: [test]
needs: [test-windows, test-macos]
strategy:
fail-fast: false
matrix:
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@
[submodule "vendor/spdlog"]
path = vendor/spdlog
url = git@github.com:gabime/spdlog.git
[submodule "vendor/googletest"]
path = vendor/googletest
url = git@github.com:google/googletest.git
48 changes: 29 additions & 19 deletions foo_input_ncm.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@
/* Begin PBXFileReference section */
A35F93C02BDBF18200ABAABA /* context_menu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = context_menu.cpp; sourceTree = "<group>"; };
A35F93C12BDBF18200ABAABA /* context_menu.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = context_menu.hpp; sourceTree = "<group>"; };
A35F93C22BDBF18200ABAABA /* extraction_process.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = extraction_process.hpp; sourceTree = "<group>"; };
A35F93C62BDDF04600ABAABA /* log.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = log.hpp; sourceTree = "<group>"; };
A35F93C72BDDF04600ABAABA /* consts.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = consts.hpp; sourceTree = "<group>"; };
A35F94912BE17E3100ABAABA /* foo_input_ncm_tests.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = foo_input_ncm_tests.xcodeproj; path = test/unit/macos/foo_input_ncm_tests.xcodeproj; sourceTree = "<group>"; };
A3B7380C2BCCBC6700DF7424 /* foo_input_ncm.component */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = foo_input_ncm.component; sourceTree = BUILT_PRODUCTS_DIR; };
A3B738302BCCCF1A00DF7424 /* libpfc-Mac.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = "libpfc-Mac.a"; sourceTree = BUILT_PRODUCTS_DIR; };
A3B738732BCE313B00DF7424 /* libfoobar2000_component_client.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libfoobar2000_component_client.a; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -98,17 +98,30 @@
children = (
A35F93C02BDBF18200ABAABA /* context_menu.cpp */,
A35F93C12BDBF18200ABAABA /* context_menu.hpp */,
A35F93C22BDBF18200ABAABA /* extraction_process.hpp */,
);
name = ui;
path = src/ui;
path = ui;
sourceTree = "<group>";
};
A35F94362BE167A100ABAABA /* Tests */ = {
isa = PBXGroup;
children = (
A35F94912BE17E3100ABAABA /* foo_input_ncm_tests.xcodeproj */,
);
name = Tests;
sourceTree = "<group>";
};
A35F94922BE17E3100ABAABA /* Products */ = {
isa = PBXGroup;
children = (
);
name = Products;
sourceTree = "<group>";
};
A3B738032BCCBC6700DF7424 = {
isa = PBXGroup;
children = (
A35F93C32BDBF18200ABAABA /* ui */,
A3B738962BCE497400DF7424 /* src */,
A35F94362BE167A100ABAABA /* Tests */,
A3B7380D2BCCBC6700DF7424 /* Products */,
A3B738292BCCCF1A00DF7424 /* Frameworks */,
);
Expand Down Expand Up @@ -168,6 +181,7 @@
A3B738962BCE497400DF7424 /* src */ = {
isa = PBXGroup;
children = (
A35F93C32BDBF18200ABAABA /* ui */,
A3B7387E2BCE497400DF7424 /* cipher */,
A3B738822BCE497400DF7424 /* common */,
A3B7388C2BCE497400DF7424 /* album_art_extractor.cpp */,
Expand Down Expand Up @@ -230,6 +244,12 @@
minimizedProjectReferenceProxies = 1;
productRefGroup = A3B7380D2BCCBC6700DF7424 /* Products */;
projectDirPath = "";
projectReferences = (
{
ProductGroup = A35F94922BE17E3100ABAABA /* Products */;
ProjectRef = A35F94912BE17E3100ABAABA /* foo_input_ncm_tests.xcodeproj */;
},
);
projectRoot = "";
targets = (
A3B7380B2BCCBC6700DF7424 /* foo_input_ncm */,
Expand Down Expand Up @@ -328,12 +348,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
"src/**",
vendor/,
vendor/sdk,
vendor/sdk/foobar2000,
);
HEADER_SEARCH_PATHS = "";
INCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIES = src;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.music";
LIBRARY_SEARCH_PATHS = "";
Expand Down Expand Up @@ -402,12 +417,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
"src/**",
vendor/,
vendor/sdk,
vendor/sdk/foobar2000,
);
HEADER_SEARCH_PATHS = "";
INCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIES = src;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.music";
LIBRARY_SEARCH_PATHS = "";
Expand Down Expand Up @@ -446,7 +456,7 @@
INFOPLIST_KEY_NSPrincipalClass = "";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
MACOSX_DEPLOYMENT_TARGET = 13.3;
MARKETING_VERSION = 0.4.0;
MARKETING_VERSION = 0.5.0;
ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = "pnck.foo-input-ncm";
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down Expand Up @@ -481,7 +491,7 @@
INFOPLIST_KEY_NSPrincipalClass = "";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
MACOSX_DEPLOYMENT_TARGET = 13.3;
MARKETING_VERSION = 0.4.0;
MARKETING_VERSION = 0.5.0;
ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = "pnck.foo-input-ncm";
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down
3 changes: 3 additions & 0 deletions src/cipher/aes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ namespace fb2k_ncm::cipher
{ impl.decrypt_all() } -> std::same_as<decltype(impl) &>;
{ impl.decrypt_chunk(std::declval<size_t>()) } -> std::same_as<decltype(impl) &>;
{ impl.decrypt_next() } -> std::same_as<decltype(impl) &>;
{ impl.encrypt_all() } -> std::same_as<decltype(impl) &>;
{ impl.encrypt_chunk(std::declval<size_t>()) } -> std::same_as<decltype(impl) &>;
{ impl.encrypt_next() } -> std::same_as<decltype(impl) &>;
{ impl.finish() };
// properties
{ impl.chain_mode() } -> std::same_as<aes_chain_mode>;
Expand Down
71 changes: 51 additions & 20 deletions src/cipher/aes_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ size_t AES_context_common::outputted_len() const {
return finished_outputted_;
}
if (std::holds_alternative<uint8_t *>(output_buffer_)) {
return output_head_ - std::get<uint8_t *>(output_buffer_);
return std::distance(std::get<uint8_t *>(output_buffer_), output_head_);
} else {
return output_head_ - std::get<std::vector<uint8_t> *>(output_buffer_)->data();
return std::distance(std::get<std::vector<uint8_t> *>(output_buffer_)->data(), output_head_);
}
}
size_t AES_context_common::output_remain() const {
Expand Down Expand Up @@ -88,49 +88,79 @@ void AES_context_common::set_chain_mode(aes_chain_mode mode) {
chain_mode_ = mode;
}

void AES_context_common::decrypt_next() {
if (!in_progress_ || is_done_) {
throw cipher_error("Can't repeat decryption", status_);
}
return decrypt_chunk(last_chunk_size_);
}

void AES_context_common::decrypt_all() {
if (is_done_) {
throw cipher_error("Context finished", status_);
}
if (!in_progress_) {
do_prepare();
}
return decrypt_chunk(input_remain());
do {
decrypt_chunk(input_remain());
} while (input_remain());
}

void AES_context_common::decrypt_chunk(size_t chunk_size) {
void AES_context_common::encrypt_next() {
if (!in_progress_ || is_done_) {
throw cipher_error("Can't repeat encryption", status_);
}
return encrypt_chunk(last_chunk_size_);
}

void AES_context_common::encrypt_all() {
if (is_done_) {
throw cipher_error("Context finished", status_);
}
if (!in_progress_) {
do_prepare();
}
do {
encrypt_chunk(input_remain());
} while (input_remain());
}

template <AES_context_common::OP op>
void AES_context_common::universal_chunk_op(size_t chunk_size) {
if (is_done_) {
throw cipher_error("Context finished", status_);
}
if (!in_progress_) {
do_prepare();
in_progress_ = true;
}
size_t original_output_buffer_size = output_buffer_size_;
size_t stashed_output_buffer_size = output_buffer_size_;
if (std::holds_alternative<std::vector<uint8_t> *>(output_buffer_)) {
auto p = std::get<std::vector<uint8_t> *>(output_buffer_);
auto head_off = output_head_ - p->data();
p->resize(p->size() + aligned(chunk_size));
auto output_head_n = std::distance(p->data(), output_head_);
p->resize(outputted_len() + aligned(chunk_size));
output_buffer_size_ = p->size();
// resize vector may realloc the space, thus record the offset of output head and reset head from it
output_head_ = p->data() + head_off;
output_head_ = p->data() + output_head_n;
}
if (auto remain = input_remain(); chunk_size > remain) {
chunk_size = remain;
}
size_t result_size = 0;
switch (chain_mode_) {
case aes_chain_mode::CBC:
result_size = do_decrypt(aes_chain_mode::CBC, output_head_, output_remain(), input_head_, chunk_size);
switch (op) {
case OP::DEC:
result_size = do_decrypt(chain_mode_, output_head_, output_remain(), input_head_, chunk_size);
break;
case aes_chain_mode::ECB:
result_size = do_decrypt(aes_chain_mode::ECB, output_head_, output_remain(), input_head_, chunk_size);
case OP::ENC:
result_size = do_encrypt(chain_mode_, output_head_, output_remain(), input_head_, chunk_size);
break;
}
last_chunk_size_ = result_size;
if (std::holds_alternative<std::vector<uint8_t> *>(output_buffer_)) {
// shrink output vector to exact size
auto p = std::get<std::vector<uint8_t> *>(output_buffer_);
p->resize(original_output_buffer_size + result_size);
p->resize(stashed_output_buffer_size + result_size);
output_buffer_size_ = p->size();
}
input_head_ += chunk_size;
Expand All @@ -140,11 +170,12 @@ void AES_context_common::decrypt_chunk(size_t chunk_size) {
}
}

void AES_context_common::decrypt_next() {
if (!in_progress_ || is_done_) {
throw cipher_error("Can't repeat decryption", status_);
}
return decrypt_chunk(last_chunk_size_);
void AES_context_common::decrypt_chunk(size_t chunk_size) {
universal_chunk_op<OP::DEC>(chunk_size);
}

void AES_context_common::encrypt_chunk(size_t chunk_size) {
universal_chunk_op<OP::ENC>(chunk_size);
}

void AES_context_common::finish() {
Expand All @@ -154,7 +185,7 @@ void AES_context_common::finish() {
is_done_ = true;
in_progress_ = false;

input_buffer_ = (const uint8_t *)nullptr;
input_buffer_ = static_cast<const uint8_t *>(nullptr);
input_head_ = nullptr;
input_buffer_size_ = 0;

Expand Down
15 changes: 13 additions & 2 deletions src/cipher/aes_common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace fb2k_ncm::cipher::details
virtual void do_prepare() = 0;
// additional finish operations
virtual void do_finish() = 0;
// virtual size_t do_encrypt(uint8_t *dst, size_t cb_dst, const uint8_t *src, const size_t cb_src) = 0;
virtual size_t do_encrypt(const aes_chain_mode M, uint8_t *dst, const size_t cb_dst, const uint8_t *src, const size_t cb_src) = 0;
virtual size_t do_decrypt(const aes_chain_mode M, uint8_t *dst, const size_t cb_dst, const uint8_t *src, const size_t cb_src) = 0;

public:
Expand Down Expand Up @@ -63,12 +63,23 @@ namespace fb2k_ncm::cipher::details
void set_output(std::vector<uint8_t> &output);
void set_output(uint8_t *output, size_t size);
void set_chain_mode(aes_chain_mode mode);
void decrypt_all();
void decrypt_chunk(size_t chunk_size);
void decrypt_next();
void decrypt_all();
void encrypt_chunk(size_t chunk_size);
void encrypt_next();
void encrypt_all();
void finish();

private:
// reduce reapeted code
enum class OP { DEC, ENC };

template <OP op>
void universal_chunk_op(size_t chunk_size);

protected:
// expose chainable operations for inherited classes, since we are not using virtual functions
template <typename D, typename F, typename... ARGS>
requires std::derived_from<D, AES_context_common>
inline D &make_chain_op(F &&f, ARGS &&...args) {
Expand Down
Loading

0 comments on commit 643df72

Please sign in to comment.