Initial commit
This commit is contained in:
commit
9edfaa9105
8 changed files with 471 additions and 0 deletions
20
.clang-format
Normal file
20
.clang-format
Normal file
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
BasedOnStyle: Chromium
|
||||
IndentWidth: 4
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
ColumnLimit: 140
|
||||
---
|
||||
Language: Cpp
|
||||
AlignConsecutiveMacros: None
|
||||
AlignConsecutiveAssignments: None
|
||||
BraceWrapping:
|
||||
AfterFunction: false
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: false
|
||||
SplitEmptyNamespace: false
|
||||
AfterControlStatement: Never
|
||||
BreakBeforeBraces: Custom
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
Cpp11BracedListStyle: false
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AllowShortBlocksOnASingleLine: Empty
|
145
.gitignore
vendored
Normal file
145
.gitignore
vendored
Normal file
|
@ -0,0 +1,145 @@
|
|||
# Created by https://www.toptal.com/developers/gitignore/api/c++,cmake,clion+all
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=c++,cmake,clion+all
|
||||
|
||||
### C++ ###
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
### CLion+all ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/jarRepositories.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# SonarLint plugin
|
||||
.idea/sonarlint/
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### CLion+all Patch ###
|
||||
# Ignore everything but code style settings and run configurations
|
||||
# that are supposed to be shared within teams.
|
||||
|
||||
.idea/*
|
||||
|
||||
!.idea/codeStyles
|
||||
!.idea/runConfigurations
|
||||
|
||||
### CMake ###
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
Testing
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
|
||||
### CMake Patch ###
|
||||
# External projects
|
||||
*-prefix/
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/c++,cmake,clion+all
|
||||
.idea/
|
||||
matrix-security-key.txt
|
9
.gitmodules
vendored
Normal file
9
.gitmodules
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
[submodule "quirc"]
|
||||
path = quirc
|
||||
url = https://gitea.ahur.ac/timoreo/quirc.git
|
||||
[submodule "olm"]
|
||||
path = olm
|
||||
url = https://gitea.ahur.ac/timoreo/olm.git
|
||||
[submodule "matrix-3ds-sdk"]
|
||||
path = matrix-3ds-sdk
|
||||
url = https://gitea.ahur.ac/timoreo/matrix-3ds-sdk.git
|
35
CMakeLists.txt
Normal file
35
CMakeLists.txt
Normal file
|
@ -0,0 +1,35 @@
|
|||
set(CMAKE_TOOLCHAIN_FILE "/opt/devkitpro/cmake/3DS.cmake")
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
project(matrix-3ds-client)
|
||||
# for compiling a part of the pica asm
|
||||
enable_language(ASM)
|
||||
|
||||
add_subdirectory(matrix-3ds-sdk)
|
||||
add_subdirectory(olm)
|
||||
add_executable(matrix-3ds-client src/main.cpp)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(jansson REQUIRED IMPORTED_TARGET jansson)
|
||||
target_link_libraries(matrix-3ds-client curl PkgConfig::jansson matrix-3ds-sdk olm citro2d citro3d)
|
||||
target_include_directories(matrix-3ds-client PUBLIC src "${DEVKITPRO}/portlibs/3ds/include/" ${CMAKE_CURRENT_BINARY_DIR}/include)
|
||||
|
||||
set(SMDH_AUTHOR timoreo)
|
||||
set(SMDH_DESCRIPTION "A kinda basic matrix 3ds client")
|
||||
ctr_create_3dsx(matrix-3ds-client SMDH)
|
||||
#add_custom_command(
|
||||
#OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/vshader.shbin"
|
||||
#COMMAND "${CTR_PICASSO_EXE}" -o "${CMAKE_CURRENT_BINARY_DIR}/vshader.shbin" src/imgui/vshader.v.pica
|
||||
#DEPENDS src/imgui/vshader.v.pica
|
||||
#WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
#COMMENT "Building shader library src/imgui/vshader.v.pica"
|
||||
#VERBATIM
|
||||
#)
|
||||
#add_custom_command(
|
||||
# OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/include/vshader_shbin.h" "${CMAKE_CURRENT_BINARY_DIR}/vshader_shbin.s"
|
||||
# COMMAND "${DKP_BIN2S}" "${CMAKE_CURRENT_BINARY_DIR}/vshader.shbin" -H "${CMAKE_CURRENT_BINARY_DIR}/include/vshader_shbin.h" > "${CMAKE_CURRENT_BINARY_DIR}/vshader_shbin.s"
|
||||
# DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/vshader.shbin"
|
||||
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
# COMMENT "Compiling to header src/imgui/vshader.v.pica"
|
||||
# VERBATIM
|
||||
#)
|
||||
#add_custom_target(vshader DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/include/vshader_shbin.h")
|
||||
#add_dependencies(matrix-3ds-client vshader)
|
1
matrix-3ds-sdk
Submodule
1
matrix-3ds-sdk
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 833de44d81c7a79bb1327cd829116c1cce3861f6
|
1
olm
Submodule
1
olm
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit cb3fe622ae742674436e361bdefe534d300ced47
|
1
quirc
Submodule
1
quirc
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 71c1eebcb77e7bf37c0a89c0b6ab3b39df2870ee
|
259
src/main.cpp
Normal file
259
src/main.cpp
Normal file
|
@ -0,0 +1,259 @@
|
|||
#include <3ds.h>
|
||||
#include <matrixclient.h>
|
||||
#include <olm/base64.h>
|
||||
#include <olm/olm.h>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#define PREFIX_DIRECTORY "./matrix-storage"
|
||||
#define TOKEN_FILENAME "token"
|
||||
#define DEVICE_ID "XREVXEDLHU"
|
||||
|
||||
class Store : public Matrix::Store {
|
||||
void setSyncToken(std::string token) override {
|
||||
|
||||
}
|
||||
std::string getSyncToken() override {
|
||||
return "";
|
||||
}
|
||||
void setFilterId(std::string filterId) override {}
|
||||
std::string getFilterId() override { return ""; }
|
||||
};
|
||||
PrintConsole* bottom = new PrintConsole;
|
||||
int main() {
|
||||
fsInit();
|
||||
gfxInitDefault();
|
||||
consoleInit(GFX_BOTTOM, bottom);
|
||||
uint8_t major, minor, patch;
|
||||
olm_get_library_version(&major, &minor, &patch);
|
||||
std::cout << "Using olm " << major << "." << minor << "." << patch << std::endl;
|
||||
std::filesystem::create_directories(PREFIX_DIRECTORY);
|
||||
chdir(PREFIX_DIRECTORY);
|
||||
Matrix::Client* client = nullptr;
|
||||
while (aptMainLoop()) {
|
||||
gspWaitForVBlank();
|
||||
hidScanInput();
|
||||
u32 kDown = hidKeysDown();
|
||||
|
||||
if (kDown & KEY_START) {
|
||||
break;
|
||||
}
|
||||
if (kDown & KEY_SELECT) {
|
||||
std::ifstream stream{TOKEN_FILENAME, std::fstream::in};
|
||||
if(!stream.is_open()){
|
||||
// File dosn't exist, log in
|
||||
client = new Matrix::Client("https://matrix.timoreo.fr", "", nullptr); // &s
|
||||
if (client->login("timoreo-3ds", "***REMOVED***", DEVICE_ID)) {
|
||||
// logged in
|
||||
puts("Logged in !!");
|
||||
std::ofstream ostr{TOKEN_FILENAME, std::fstream::out};
|
||||
ostr << client->getToken() << std::endl;
|
||||
ostr.close();
|
||||
} else {
|
||||
// wrong endpoint or combo
|
||||
puts("Wrong combo !");
|
||||
goto wrong;
|
||||
}
|
||||
}else{
|
||||
std::string line;
|
||||
std::getline(stream, line);
|
||||
client = new Matrix::Client("https://matrix.timoreo.fr", line, nullptr); // &s
|
||||
stream.close();
|
||||
}
|
||||
auto userid = client->getUserId();
|
||||
if(userid.empty()){
|
||||
puts("Userid is empty ! Wrong token ?");
|
||||
goto wrong;
|
||||
}
|
||||
// Load encryption here
|
||||
|
||||
client->setRoomInfoCallback([](std::string roomId, Matrix::RoomInfo info){
|
||||
consoleSelect(bottom);
|
||||
printf("Joined room %s named %s\n", roomId.c_str(), info.name.c_str());
|
||||
});
|
||||
client->setEventCallback([](std::string roomId, json_t* event){
|
||||
consoleSelect(bottom);
|
||||
printf("Event in room %s\n", roomId.c_str());
|
||||
printf("%s\n", json_dumps(event, JSON_ENSURE_ASCII));
|
||||
});
|
||||
client->startSyncLoop();
|
||||
}
|
||||
wrong:
|
||||
;
|
||||
}
|
||||
if(client != nullptr){
|
||||
client->stopSyncLoop();
|
||||
// DO NOT LOGOUT, KEEP ENCRYPTION KEYS
|
||||
}
|
||||
gfxExit();
|
||||
fsExit();
|
||||
}
|
||||
void generate_otk(OlmAccount* acc, size_t otkcount) {
|
||||
size_t len = olm_account_generate_one_time_keys_random_length(acc, otkcount);
|
||||
std::unique_ptr<char> otkrandom = std::make_unique<char>(len);
|
||||
PS_GenerateRandomBytes(otkrandom.get(), len);
|
||||
olm_account_generate_one_time_keys(acc, otkcount, otkrandom.get(), len);
|
||||
}
|
||||
void generate_otk(OlmAccount* acc){
|
||||
size_t otkcount = olm_account_max_number_of_one_time_keys(acc)/2;
|
||||
generate_otk(acc, otkcount);
|
||||
}
|
||||
|
||||
|
||||
void generate_device_key(OlmAccount* acc) {
|
||||
size_t len = olm_create_account_random_length(acc);
|
||||
std::unique_ptr<char> random = std::make_unique<char>(len);
|
||||
PS_GenerateRandomBytes(random.get(), len);
|
||||
olm_create_account(acc, random.get(), len);
|
||||
}
|
||||
|
||||
void generate_fallback_key(OlmAccount* acc) {
|
||||
size_t len = olm_account_generate_fallback_key_random_length(acc);
|
||||
std::unique_ptr<char> random = std::make_unique<char>(len);
|
||||
PS_GenerateRandomBytes(random.get(), len);
|
||||
olm_account_generate_fallback_key(acc, random.get(), len);
|
||||
}
|
||||
|
||||
// Make sure to remove any unsigned and signatures block before doing this !
|
||||
void sign_json(Matrix::Client& client, OlmAccount* acc, json_t* json) {
|
||||
char* jsonStr = json_dumps(json, JSON_COMPACT | JSON_SORT_KEYS);
|
||||
size_t signlen = olm_account_signature_length(acc);
|
||||
char* signature = reinterpret_cast<char*>(malloc(signlen));
|
||||
olm_account_sign(acc, jsonStr, strlen(jsonStr), signature, signlen);
|
||||
free(jsonStr);
|
||||
json_t * signitem = json_object();
|
||||
json_object_set(signitem, ("ed25519:" + client.getDeviceId()).c_str(), json_stringn(signature, signlen));
|
||||
json_t * signobj = json_object();
|
||||
json_object_set(signobj, client.getUserId().c_str(), signitem);
|
||||
json_object_set(json, "signatures", signobj);
|
||||
}
|
||||
json_t* get_device_keys(Matrix::Client& client, OlmAccount* acc) {
|
||||
json_t * device_keys = json_object();
|
||||
json_t * algorithms = json_array();
|
||||
json_array_append_new(algorithms, json_string("m.olm.v1.curve25519-aes-sha2"));
|
||||
json_array_append_new(algorithms, json_string("m.megolm.v1.aes-sha2"));
|
||||
json_object_set_new(device_keys, "algorithms", algorithms);
|
||||
json_object_set_new(device_keys, "device_id", json_string(client.getDeviceId().c_str()));
|
||||
json_object_set_new(device_keys, "user_id", json_string(client.getUserId().c_str()));
|
||||
|
||||
// Extract keys
|
||||
size_t acclen = olm_account_identity_keys_length(acc);
|
||||
auto acckeys = reinterpret_cast<char *>(malloc(acclen));
|
||||
olm_account_identity_keys(acc, acckeys, acclen);
|
||||
json_t * keys = json_loads(acckeys, 0, nullptr);
|
||||
free(acckeys);
|
||||
|
||||
// Merge keys
|
||||
json_object_set_new(device_keys, ("ed25519:" + client.getDeviceId()).c_str(), json_object_get(keys, "ed25519"));
|
||||
json_object_set_new(device_keys, ("curve25519:" + client.getDeviceId()).c_str(), json_object_get(keys, "curve25519"));
|
||||
json_decref(keys);
|
||||
return device_keys;
|
||||
}
|
||||
json_t* get_fallback_keys(Matrix::Client& client, OlmAccount* acc) {
|
||||
json_t * fallback_keys = json_object();
|
||||
// Extract keys
|
||||
size_t acclen = olm_account_unpublished_fallback_key_length(acc);
|
||||
auto acckeys = reinterpret_cast<char *>(malloc(acclen));
|
||||
olm_account_unpublished_fallback_key(acc, acckeys, acclen);
|
||||
json_t * keys = json_loads(acckeys, 0, nullptr);
|
||||
free(acckeys);
|
||||
json_t* keyobj = json_object_get(keys, "curve25519");
|
||||
void* iter = json_object_iter(keyobj);
|
||||
|
||||
while (iter != nullptr) {
|
||||
const char* keyid = json_object_iter_key(iter);
|
||||
json_t* keyval = json_object_iter_value(iter);
|
||||
json_t* tosign = json_object();
|
||||
json_object_set_new(tosign, "key", keyval);
|
||||
// object is fallback key
|
||||
json_object_set_new(tosign, "fallback", json_true());
|
||||
|
||||
sign_json(client, acc, tosign);
|
||||
json_object_set_new(fallback_keys, (std::string("signed_curve25519:") + keyid).c_str(), tosign );
|
||||
iter = json_object_iter_next(keyobj, iter);
|
||||
}
|
||||
json_decref(keys);
|
||||
return fallback_keys;
|
||||
}
|
||||
OlmAccount* load_account(Matrix::Client client){
|
||||
auto* acc = static_cast<OlmAccount*>(malloc(olm_account_size()));
|
||||
olm_account(acc);
|
||||
std::filebuf filestorage;
|
||||
|
||||
// std::FILE* file = std::fopen("secret-keys", "r");
|
||||
std::ifstream ifs("secret-keys", std::ios::binary|std::ios::ate);
|
||||
if (ifs.is_open()) {
|
||||
// Generate and save to file
|
||||
// Device keys
|
||||
generate_device_key(acc);
|
||||
// One time keys
|
||||
generate_otk(acc);
|
||||
// Fallback keys
|
||||
generate_fallback_key(acc);
|
||||
|
||||
// Convert to pickled format
|
||||
size_t pacclen = olm_pickle_account_length(acc);
|
||||
|
||||
auto m = reinterpret_cast<uint8_t *>(malloc(pacclen));
|
||||
|
||||
olm_pickle_account(acc, nullptr, 0, m, pacclen);
|
||||
|
||||
pacclen = _olm_decode_base64(m, pacclen, m);
|
||||
FILE* file = std::fopen("secret-keys", "w");
|
||||
std::fwrite(m, pacclen, 1, file);
|
||||
|
||||
free(m);
|
||||
std::fclose(file);
|
||||
// Upload the keys ---
|
||||
// Build device key json
|
||||
json_t * upload_keys = json_object();
|
||||
|
||||
json_t* device_keys = get_device_keys(client, acc);
|
||||
// Sign keys block
|
||||
sign_json(client, acc, device_keys);
|
||||
json_object_set(upload_keys, "device_keys", device_keys);
|
||||
|
||||
json_t* fallback_keys = get_fallback_keys(client, acc);
|
||||
// fallback keys are already signed
|
||||
json_object_set(upload_keys, "fallback_keys", fallback_keys);
|
||||
json_decref(upload_keys);
|
||||
|
||||
} else {
|
||||
// Load from file
|
||||
std::ifstream::pos_type fsize = ifs.tellg();
|
||||
if(fsize == -1){
|
||||
std::cout << "What." << std::endl;
|
||||
return acc;
|
||||
}
|
||||
size_t overhead = fsize/4;
|
||||
auto data = reinterpret_cast<uint8_t*>(malloc(overhead + fsize));
|
||||
ifs.seekg(0, std::ios::beg);
|
||||
ifs.read(reinterpret_cast<char*>(overhead + data), static_cast<std::streamsize>(fsize));
|
||||
_olm_encode_base64(overhead + data, fsize, data);
|
||||
olm_unpickle_account(acc, nullptr, 0, data, overhead + fsize);
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
void make_keys(OlmAccount* acc){
|
||||
|
||||
// Generate Device Keys
|
||||
size_t keylen = olm_account_identity_keys_length(acc);
|
||||
//ssize_t datalen = _olm_encode_base64_length(keylen);
|
||||
std::unique_ptr<uint8_t> identity_keys = std::make_unique<uint8_t>(keylen);
|
||||
olm_account_identity_keys(acc, identity_keys.get(), keylen);
|
||||
// _olm_decode_base64(identity_keys.get(), datalen, identity_keys.get());
|
||||
// Save keys to disk
|
||||
std::ofstream storage("account-keys");
|
||||
storage << identity_keys.get() << std::endl;
|
||||
storage.close();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return acc;
|
||||
}
|
Loading…
Reference in a new issue