From d682f3010f70f571e6c6d0876417fcd71468bf52 Mon Sep 17 00:00:00 2001 From: red binder Date: Mon, 22 Jun 2026 10:31:09 +0200 Subject: [PATCH 01/29] implement 3DS NintendoCreateAccount support --- Cargo.lock | 632 ++++++++++++++++++++++++++- rnex-core/Cargo.toml | 1 + rnex-core/src/nex/friends_handler.rs | 67 ++- 3 files changed, 660 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 496b346..6245ab0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,15 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -515,6 +524,49 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" +dependencies = [ + "axum-core", + "bytes", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "sync_wrapper", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", +] + [[package]] name = "base16ct" version = "0.1.1" @@ -1046,6 +1098,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "etcetera" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96" +dependencies = [ + "cfg-if", + "windows-sys 0.61.2", +] + [[package]] name = "event-listener" version = "5.4.1" @@ -1079,6 +1141,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "flate2" version = "1.1.9" @@ -1100,6 +1168,17 @@ dependencies = [ "spin 0.9.8", ] +[[package]] +name = "flume" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e139bc46ca777eb5efaf62df0ab8cc5fd400866427e56c68b22e414e53bd3be" +dependencies = [ + "futures-core", + "futures-sink", + "spin 0.9.8", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1365,6 +1444,15 @@ dependencies = [ "hashbrown 0.15.5", ] +[[package]] +name = "hashlink" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824e001ac4f3012dd16a264bec811403a67ca9deb6c102fc5049b32c4574b35f" +dependencies = [ + "hashbrown 0.16.1", +] + [[package]] name = "heck" version = "0.5.0" @@ -1386,6 +1474,15 @@ dependencies = [ "hmac 0.12.1", ] +[[package]] +name = "hkdf" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" +dependencies = [ + "hmac 0.13.0", +] + [[package]] name = "hmac" version = "0.12.1" @@ -1527,6 +1624,7 @@ dependencies = [ "http 1.4.0", "http-body 1.0.1", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -1565,6 +1663,19 @@ dependencies = [ "tower-service", ] +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper 1.9.0", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.20" @@ -1748,6 +1859,15 @@ version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" @@ -1843,6 +1963,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c7b0a3383c2a1002d11349c92c85a666a5fb679e96c79d782cf0dbe557fd6ee" +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + [[package]] name = "litemap" version = "0.8.2" @@ -1898,6 +2024,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "md-5" version = "0.10.6" @@ -1924,6 +2056,12 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -1945,6 +2083,30 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] +name = "nex-account" +version = "0.1.3" +source = "sparse+https://crates.spbr.net/api/v1/crates/" +checksum = "16be02914ed26b15f68fc8c0c11f719ca9796e90be5899523baad8a3bdf0069b" +dependencies = [ + "hmac 0.13.0", + "log", + "md-5 0.11.0", + "prost", + "simplelog", + "sqlx 0.9.0", + "tokio", + "tonic", + "tonic-prost", + "tonic-prost-build", +] + [[package]] name = "num-bigint-dig" version = "0.8.6" @@ -2085,6 +2247,37 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -2183,6 +2376,59 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prost" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" +dependencies = [ + "heck", + "itertools", + "log", + "multimap", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "pulldown-cmark", + "pulldown-cmark-to-cmark", + "regex", + "syn 2.0.117", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "prost-types" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f94967dc7688f3054c7fac87473ffae4cc4c3904800e2d9f5b857246d8963b0a" +dependencies = [ + "prost", +] + [[package]] name = "proxy" version = "0.1.0" @@ -2258,6 +2504,26 @@ dependencies = [ "v-byte-helpers", ] +[[package]] +name = "pulldown-cmark" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e" +dependencies = [ + "bitflags", + "memchr", + "unicase", +] + +[[package]] +name = "pulldown-cmark-to-cmark" +version = "22.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50793def1b900256624a709439404384204a5dc3a6ec580281bfaac35e882e90" +dependencies = [ + "pulldown-cmark", +] + [[package]] name = "quote" version = "1.0.45" @@ -2382,12 +2648,41 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + [[package]] name = "regex-lite" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + [[package]] name = "rfc6979" version = "0.3.1" @@ -2434,6 +2729,7 @@ dependencies = [ "log", "macros 0.0.0", "md-5 0.10.6", + "nex-account", "once_cell", "paste", "rand 0.10.1", @@ -2442,7 +2738,7 @@ dependencies = [ "serde_json", "sha2 0.10.9", "simplelog", - "sqlx", + "sqlx 0.8.6", "thiserror", "tokio", "typenum", @@ -2480,6 +2776,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustls" version = "0.21.12" @@ -2859,11 +3168,24 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" dependencies = [ - "sqlx-core", - "sqlx-macros", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", + "sqlx-core 0.8.6", + "sqlx-macros 0.8.6", + "sqlx-mysql 0.8.6", + "sqlx-postgres 0.8.6", + "sqlx-sqlite 0.8.6", +] + +[[package]] +name = "sqlx" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "378620ccc25c62c89d8be1c819e76a88d59bdcc3304733330788948e619bfd71" +dependencies = [ + "sqlx-core 0.9.0", + "sqlx-macros 0.9.0", + "sqlx-mysql 0.9.0", + "sqlx-postgres 0.9.0", + "sqlx-sqlite 0.9.0", ] [[package]] @@ -2884,7 +3206,7 @@ dependencies = [ "futures-io", "futures-util", "hashbrown 0.15.5", - "hashlink", + "hashlink 0.10.0", "indexmap", "log", "memchr", @@ -2901,6 +3223,40 @@ dependencies = [ "url", ] +[[package]] +name = "sqlx-core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b44e85bf579a8eeb4ceaa77a3a523baf2bf0e9bac7e40f405d537b5d2d5ccb" +dependencies = [ + "base64", + "bytes", + "cfg-if", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.16.1", + "hashlink 0.11.1", + "indexmap", + "log", + "memchr", + "percent-encoding", + "serde", + "serde_json", + "sha2 0.10.9", + "smallvec", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "url", +] + [[package]] name = "sqlx-macros" version = "0.8.6" @@ -2909,8 +3265,21 @@ checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" dependencies = [ "proc-macro2", "quote", - "sqlx-core", - "sqlx-macros-core", + "sqlx-core 0.8.6", + "sqlx-macros-core 0.8.6", + "syn 2.0.117", +] + +[[package]] +name = "sqlx-macros" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd2b84f2bc39a5705ef27ec785a11c934a41bbd4a24941e257927cddc26b60bf" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core 0.9.0", + "sqlx-macros-core 0.9.0", "syn 2.0.117", ] @@ -2930,15 +3299,41 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.9", - "sqlx-core", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", + "sqlx-core 0.8.6", + "sqlx-mysql 0.8.6", + "sqlx-postgres 0.8.6", + "sqlx-sqlite 0.8.6", "syn 2.0.117", "tokio", "url", ] +[[package]] +name = "sqlx-macros-core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb8d96de5fdc85a5c4ec813432b523ec637e80ba98f046555f75f7908ddac7c3" +dependencies = [ + "cfg-if", + "dotenvy", + "either", + "heck", + "hex", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2 0.10.9", + "sqlx-core 0.9.0", + "sqlx-mysql 0.9.0", + "sqlx-postgres 0.9.0", + "sqlx-sqlite 0.9.0", + "syn 2.0.117", + "thiserror", + "tokio", + "url", +] + [[package]] name = "sqlx-mysql" version = "0.8.6" @@ -2961,7 +3356,7 @@ dependencies = [ "futures-util", "generic-array", "hex", - "hkdf", + "hkdf 0.12.4", "hmac 0.12.1", "itoa", "log", @@ -2975,11 +3370,37 @@ dependencies = [ "sha1 0.10.6", "sha2 0.10.9", "smallvec", - "sqlx-core", + "sqlx-core 0.8.6", "stringprep", "thiserror", "tracing", - "whoami", + "whoami 1.6.1", +] + +[[package]] +name = "sqlx-mysql" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90b8020fe17c5f2c245bfa2505d7ef59c5604839527c740266ad2214acebea27" +dependencies = [ + "bitflags", + "byteorder", + "bytes", + "crc", + "digest 0.11.3", + "dotenvy", + "either", + "futures-core", + "futures-util", + "generic-array", + "log", + "percent-encoding", + "serde", + "sha1 0.11.0", + "sha2 0.11.0", + "sqlx-core 0.9.0", + "thiserror", + "tracing", ] [[package]] @@ -2995,12 +3416,12 @@ dependencies = [ "chrono", "crc", "dotenvy", - "etcetera", + "etcetera 0.8.0", "futures-channel", "futures-core", "futures-util", "hex", - "hkdf", + "hkdf 0.12.4", "hmac 0.12.1", "home", "itoa", @@ -3013,11 +3434,46 @@ dependencies = [ "serde_json", "sha2 0.10.9", "smallvec", - "sqlx-core", + "sqlx-core 0.8.6", "stringprep", "thiserror", "tracing", - "whoami", + "whoami 1.6.1", +] + +[[package]] +name = "sqlx-postgres" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87a2bdd6e83f6b3ea525ca9fee568030508b58355a43d0b2c1674d5f79dcd65e" +dependencies = [ + "atoi", + "base64", + "bitflags", + "byteorder", + "crc", + "dotenvy", + "etcetera 0.11.0", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf 0.13.0", + "hmac 0.13.0", + "itoa", + "log", + "md-5 0.11.0", + "memchr", + "rand 0.10.1", + "serde", + "serde_json", + "sha2 0.11.0", + "smallvec", + "sqlx-core 0.9.0", + "stringprep", + "thiserror", + "tracing", + "whoami 2.1.2", ] [[package]] @@ -3028,7 +3484,7 @@ checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" dependencies = [ "atoi", "chrono", - "flume", + "flume 0.11.1", "futures-channel", "futures-core", "futures-executor", @@ -3039,7 +3495,31 @@ dependencies = [ "percent-encoding", "serde", "serde_urlencoded", - "sqlx-core", + "sqlx-core 0.8.6", + "thiserror", + "tracing", + "url", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488e99c397a62007e4229aec669a179816339afc6d2620ca6fa420dbee2e982c" +dependencies = [ + "atoi", + "flume 0.12.0", + "form_urlencoded", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "sqlx-core 0.9.0", "thiserror", "tracing", "url", @@ -3090,6 +3570,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + [[package]] name = "synstructure" version = "0.13.2" @@ -3101,6 +3587,19 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -3276,14 +3775,91 @@ dependencies = [ "tokio", ] +[[package]] +name = "tonic" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" +dependencies = [ + "async-trait", + "axum", + "base64", + "bytes", + "h2 0.4.13", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.9.0", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "socket2 0.6.3", + "sync_wrapper", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f61875ac5293cf72e6c8cf0158086428c82c37229e98c840878f1706b0322" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tonic-prost" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0" +dependencies = [ + "bytes", + "prost", + "tonic", +] + +[[package]] +name = "tonic-prost-build" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "654e5643eff75d7f8c99197ce1440ed19a3474eada74c12bbac488b2cafdae27" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn 2.0.117", + "tempfile", + "tonic-build", +] + [[package]] name = "tower" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project-lite", + "slab", + "sync_wrapper", + "tokio", + "tokio-util", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -3361,6 +3937,12 @@ version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-bidi" version = "0.3.18" @@ -3642,6 +4224,12 @@ dependencies = [ "wasite", ] +[[package]] +name = "whoami" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "998767ef88740d1f5b0682a9c53c24431453923962269c2db68ee43788c5a40d" + [[package]] name = "winapi-util" version = "0.1.11" diff --git a/rnex-core/Cargo.toml b/rnex-core/Cargo.toml index e8ea554..0c37372 100644 --- a/rnex-core/Cargo.toml +++ b/rnex-core/Cargo.toml @@ -37,6 +37,7 @@ urlencoding = "2.1.3" futures = "0.3.32" async-trait = "0.1.89" ctor = "1.0.7" +nex-account = { version = "0.1.3", registry = "spbr" } [dev-dependencies] # criterion = "0.7.0" diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index 06b3a98..0e09005 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -1,15 +1,14 @@ +use std::env; use std::io::{Cursor, Write}; use std::ops::Deref; -use std::sync::Weak; +use std::sync::{LazyLock, Weak}; use std::sync::{Arc, atomic::AtomicU32}; use bytemuck::bytes_of; use hmac::Mac; use log::info; use macros::rmc_struct; -use rnex_core::rmc::protocols::account_management::{ - AccountManagement, RawAccountManagement, RawAccountManagementInfo, RemoteAccountManagement, -}; +use rnex_core::rmc::protocols::account_management::{AccountExtraInfo, AccountManagement, RawAccountManagement, RawAccountManagementInfo, RemoteAccountManagement}; use rnex_core::rmc::protocols::friends_wiiu::{ FriendsWiiU, RawFriendsWiiU, RawFriendsWiiUInfo, RemoteFriendsWiiU, }; @@ -49,6 +48,10 @@ use rnex_core::rmc::structures::data::Data; use crate::executables::common::get_db; +use nex_account::derive_pid_hmac; +use nex_account::grpc::nex_account_service_client::NexAccountServiceClient; +use nex_account::grpc::ActCreateInfoNoPid; + define_rmc_proto!( proto FriendsUser{ Secure, @@ -67,6 +70,9 @@ define_rmc_proto!( } ); +static NEX_ACCOUNT_URL: LazyLock = + LazyLock::new(|| env::var("NEX_ACCOUNT_ENDPOINT").expect("NEX_ACCOUNT_ENDPOINT not set")); + pub struct UserData { info: NNAInfo, presence: NintendoPresenceV2, @@ -288,28 +294,53 @@ impl AccountManagement for FriendsGuest { auth_data: Any, ) -> Result<(PID, String), ErrorCode> { println!("{}, {}, {}, {}", principal_name, key, groups, email); - if auth_data.name == "NintendoCreateAccountData" { - let Ok(data) = - NintendoCreateAccountData::deserialize(&mut Cursor::new(&auth_data.data)) - else { - return Err(ErrorCode::Authentication_InvalidParam); - }; + if let Ok(data) = auth_data.try_get_as::() { let pid = data.nna_info.principal_basic_info.pid; - info!("create account: {}", pid); + info!("create account via standard data: {}", pid); - let Ok(mut mac) = HMacMd5::new_from_slice(key.as_bytes()) else { - return Err(ErrorCode::Authentication_InvalidParam); + let nexkey: [u8; 16] = hex::decode(key) + .map_err(|_| ErrorCode::Authentication_InvalidParam)? + .as_slice() + .try_into() + .map_err(|_| ErrorCode::Authentication_InvalidParam)?; + + let mac = derive_pid_hmac(data.nna_info.principal_basic_info.pid, &nexkey); + + let hex_str = hex::encode(mac); + return Ok((pid, hex_str)); + } + + if let Ok(extra_info) = auth_data.try_get_as::() { + info!("create account via extra info"); + let mut client = NexAccountServiceClient::connect(NEX_ACCOUNT_URL.as_str()) + .await + .map_err(|e| { + eprintln!("error occurred: {:?}", e); + ErrorCode::Core_Unknown + })?; + + let nexkey: [u8; 16] = hex::decode(key) + .map_err(|_| ErrorCode::Authentication_InvalidParam)? + .as_slice() + .try_into() + .map_err(|_| ErrorCode::Authentication_InvalidParam)?; + + let new_account: ActCreateInfoNoPid = ActCreateInfoNoPid { + principal_name, + key: nexkey.into(), + email, }; - mac.write_all(bytes_of(&pid)) - .expect("failed to write to hmac???"); - let mac = mac.finalize().into_bytes(); + let pid = client.create_new_sequential_or_update_and_get_account(new_account).await.map_err(|_| ErrorCode::Core_Unknown)?.into_inner(); + + let mac = derive_pid_hmac(pid.pid, &nexkey); let hex_str = hex::encode(mac); - return Ok((pid, hex_str)); + return Ok((pid.pid, hex_str)); } - Err(ErrorCode::Core_NotImplemented) + + Err(ErrorCode::Authentication_InvalidParam) } } From db6589d361ee163b71c9f28de8867ba56f01ed20 Mon Sep 17 00:00:00 2001 From: red binder Date: Mon, 22 Jun 2026 11:55:53 +0200 Subject: [PATCH 02/29] use nexact server --- Cargo.lock | 1 + rnex-core/Cargo.toml | 1 + rnex-core/src/grpc/account.rs | 213 ++++++++++++------------------ rnex-core/src/kerberos/mod.rs | 6 +- rnex-core/src/nex/auth_handler.rs | 4 +- 5 files changed, 95 insertions(+), 130 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6245ab0..62a64e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2741,6 +2741,7 @@ dependencies = [ "sqlx 0.8.6", "thiserror", "tokio", + "tonic", "typenum", "ureq", "urlencoding", diff --git a/rnex-core/Cargo.toml b/rnex-core/Cargo.toml index 0c37372..1f4e52b 100644 --- a/rnex-core/Cargo.toml +++ b/rnex-core/Cargo.toml @@ -38,6 +38,7 @@ futures = "0.3.32" async-trait = "0.1.89" ctor = "1.0.7" nex-account = { version = "0.1.3", registry = "spbr" } +tonic = "0.14.6" [dev-dependencies] # criterion = "0.7.0" diff --git a/rnex-core/src/grpc/account.rs b/rnex-core/src/grpc/account.rs index 70aca04..e782d7b 100644 --- a/rnex-core/src/grpc/account.rs +++ b/rnex-core/src/grpc/account.rs @@ -5,8 +5,13 @@ use rnex_core::PID; use std::array::TryFromSliceError; use std::ops::Deref; use std::{env, result}; +use std::sync::LazyLock; use thiserror::Error; use tokio::task::{JoinError, spawn_blocking}; +use nex_account::grpc::nex_account_service_client::NexAccountServiceClient; +use nex_account::grpc::Pid; +use tonic::transport::Channel; + static API_KEY: Lazy = Lazy::new(|| { let key = env::var("ACCOUNT_GQL_API_KEY").expect("no graphql ip specified"); @@ -26,8 +31,10 @@ pub enum Error { RequestError(#[from] ureq::Error), #[error(transparent)] Json(#[from] json::Error), - //#[error(transparent)] - //Status(#[from] tonic::Status), + #[error(transparent)] + Status(#[from] tonic::Status), + #[error(transparent)] + Transport(#[from] tonic::transport::Error), #[error("invalid password size: {0}")] PasswordConversion(#[from] TryFromSliceError), #[error("something happened")] @@ -38,141 +45,97 @@ pub enum Error { pub type Result = result::Result; -pub struct Client; //(reqwest::Client); +static NEX_ACCOUNT_URL: LazyLock = + LazyLock::new(|| env::var("NEX_ACCOUNT_ENDPOINT").expect("NEX_ACCOUNT_ENDPOINT not set")); + +pub struct Client(NexAccountServiceClient); //(reqwest::Client); impl Client { pub async fn new() -> Result { - //Ok(Self(reqwest::ClientBuilder::new().build()?)) - Ok(Self) - } - - async fn do_request(&self, request_data: JsonValue) -> Result { - let request = ureq::post(CLIENT_URI.as_str()) - .header("X-API-Key", API_KEY.deref()) - .content_type("application/json"); - let mut response = spawn_blocking(move || request.send(request_data.to_string())).await??; - - let str_body = response.body_mut().read_to_string()?; - Ok(json::parse(&str_body)?) - /* - let mut request = reqwest::Request::new(Method::POST, Url::from_str(CLIENT_URI.as_str()).unwrap()); - - *(request.body_mut()) = Some(Body::from(request_data.to_string())); - request.headers_mut().insert("X-API-Key", HeaderValue::from_str(&API_KEY).unwrap()); - request.headers_mut().insert("Content-Type", HeaderValue::from_str("application/json").unwrap()); - - let response = self.0.execute(request).await?; - - Ok(json::parse(&response.text().await?)?) - - */ - } - - pub async fn get_nex_password(&mut self, pid: PID) -> Result<[u8; 16]> { - let req = self - .do_request(object! { - "query": r"query($pid: Int!){ - userByPid(pid: $pid){ - nexPassword - } - }", - "variables": { - "pid": pid - } - }) + let client = NexAccountServiceClient::connect(NEX_ACCOUNT_URL.as_str()) .await?; + Ok(Self(client)) + } - let Some(val) = req - .entries() - .find(|v| v.0 == "data") - .ok_or(SomethingHappened)? - .1 - .entries() - .find(|v| v.0 == "userByPid") - .ok_or(SomethingHappened)? - .1 - .entries() - .find(|v| v.0 == "nexPassword") - .ok_or(SomethingHappened)? - .1 - .as_str() - else { - return Err(SomethingHappened); - }; + pub async fn get_nex_key(&mut self, pid: PID) -> Result<[u8; 16]> { + let prekey = self.0.get_nex_key_by_pid(Pid{pid}).await?.into_inner(); - Ok(val.as_bytes().try_into().map_err(|_| SomethingHappened)?) + let nexkey: [u8; 16] = prekey.key.try_into().map_err(|_| Error::SomethingHappened)?; + + Ok(nexkey) } pub async fn get_user_level(&mut self, pid: PID) -> Result { - let req = self - .do_request(object! { - "query": r"query($pid: Int!){ - userByPid(pid: $pid){ - accountLevel - } - }", - "variables": { - "pid": pid - } - }) - .await?; + // let req = self + // .do_request(object! { + // "query": r"query($pid: Int!){ + // userByPid(pid: $pid){ + // accountLevel + // } + // }", + // "variables": { + // "pid": pid + // } + // }) + // .await?; + // + // let Some(val) = req + // .entries() + // .find(|v| v.0 == "data") + // .ok_or(SomethingHappened)? + // .1 + // .entries() + // .find(|v| v.0 == "userByPid") + // .ok_or(SomethingHappened)? + // .1 + // .entries() + // .find(|v| v.0 == "accountLevel") + // .ok_or(SomethingHappened)? + // .1 + // .as_i32() + // else { + // return Err(SomethingHappened); + // }; - let Some(val) = req - .entries() - .find(|v| v.0 == "data") - .ok_or(SomethingHappened)? - .1 - .entries() - .find(|v| v.0 == "userByPid") - .ok_or(SomethingHappened)? - .1 - .entries() - .find(|v| v.0 == "accountLevel") - .ok_or(SomethingHappened)? - .1 - .as_i32() - else { - return Err(SomethingHappened); - }; - - Ok(val) + // everyone is tester until this is implemented + Ok(0) } - pub async fn get_pid_from_token(&mut self, token: String) -> Result { - let req = self - .do_request(object! { - "query": - r"query($token: String!){ - token(tokenData: $token){ - pid - } - }", - "variables": { - "token": token - } - }) - .await?; - // this breaks switch nex servers and should be fixed eventually - let Some(val) = req - .entries() - .find(|v| v.0 == "data") - .ok_or(SomethingHappened)? - .1 - .entries() - .find(|v| v.0 == "token") - .ok_or(SomethingHappened)? - .1 - .entries() - .find(|v| v.0 == "pid") - .ok_or(SomethingHappened)? - .1 - .as_i32() - else { - return Err(SomethingHappened); - }; - - Ok(val) - } + // pub async fn get_pid_from_token(&mut self, token: String) -> Result { + // let req = self + // .do_request(object! { + // "query": + // r"query($token: String!){ + // token(tokenData: $token){ + // pid + // } + // }", + // "variables": { + // "token": token + // } + // }) + // .await?; + // // this breaks switch nex servers and should be fixed eventually + // let Some(val) = req + // .entries() + // .find(|v| v.0 == "data") + // .ok_or(SomethingHappened)? + // .1 + // .entries() + // .find(|v| v.0 == "token") + // .ok_or(SomethingHappened)? + // .1 + // .entries() + // .find(|v| v.0 == "pid") + // .ok_or(SomethingHappened)? + // .1 + // .as_i32() + // else { + // return Err(SomethingHappened); + // }; + // + // Ok(val) + // } /*pub async fn get_user_data(&mut self , pid: u32) -> Result{ let req = Request::new(GetUserDataRequest{ diff --git a/rnex-core/src/kerberos/mod.rs b/rnex-core/src/kerberos/mod.rs index 1ca0576..1a44788 100644 --- a/rnex-core/src/kerberos/mod.rs +++ b/rnex-core/src/kerberos/mod.rs @@ -28,13 +28,13 @@ pub const SESSION_KEY_LENGTH: usize = SessionLengthTy::USIZE; type Md5Hmac = Hmac; -pub fn derive_key(pid: PID, password: &[u8]) -> [u8; 16] { - let iteration_count = 65000 + pid % 1024; +pub fn derive_key(pid: PID, key: &[u8]) -> [u8; 16] { + let iteration_count = pid % 1024; // we do one iteration out here to ensure the key is always 16 bytes let mut key: [u8; 16] = { let mut md5 = Md5::new(); - md5.update(password); + md5.update(key); md5.finalize().try_into().unwrap() }; diff --git a/rnex-core/src/nex/auth_handler.rs b/rnex-core/src/nex/auth_handler.rs index a5ffe2b..643c228 100644 --- a/rnex-core/src/nex/auth_handler.rs +++ b/rnex-core/src/nex/auth_handler.rs @@ -83,7 +83,7 @@ async fn get_login_data_by_pid(pid: PID) -> Option<(PID, Box<[u8]>)> { return None; }; - let Ok(passwd) = client.get_nex_password(pid).await else { + let Ok(passwd) = client.get_nex_key(pid).await else { return None; }; @@ -128,7 +128,7 @@ impl AuthHandler { return Err(ErrorCode::Core_Exception); }; - let Ok(passwd) = client.get_nex_password(pid).await else { + let Ok(passwd) = client.get_nex_key(pid).await else { warn!("unable to get nex password for pid: {}:", pid); return Err(ErrorCode::Core_Exception); }; From 96a8bbe470e5920411c5fe1ce5bd91498ec20ff6 Mon Sep 17 00:00:00 2001 From: red binder Date: Mon, 22 Jun 2026 13:42:06 +0200 Subject: [PATCH 03/29] hash the guest user password --- rnex-core/src/nex/account.rs | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/rnex-core/src/nex/account.rs b/rnex-core/src/nex/account.rs index eb542ff..08b2cdf 100644 --- a/rnex-core/src/nex/account.rs +++ b/rnex-core/src/nex/account.rs @@ -1,3 +1,4 @@ +use md5::{Digest, Md5}; use macros::RmcSerialize; use rnex_core::PID; @@ -11,16 +12,46 @@ pub struct Account { impl Account { pub fn new(pid: PID, username: &str, passwd: &str) -> Self { + let iteration_count = 65000; + // we do one iteration out here to ensure the key is always 16 bytes + + let mut key: [u8; 16] = { + let mut md5 = Md5::new(); + md5.update(passwd); + md5.finalize().try_into().unwrap() + }; + + for _ in 1..iteration_count { + let mut md5 = Md5::new(); + md5.update(key); + key = md5.finalize().try_into().unwrap(); + } + Self { - kerbros_password: passwd.as_bytes().into(), + kerbros_password: key.into(), username: username.into(), pid, } } pub fn new_raw_password(pid: PID, username: &str, passwd: &[u8]) -> Self { + let iteration_count = 65000; + // we do one iteration out here to ensure the key is always 16 bytes + + let mut key: [u8; 16] = { + let mut md5 = Md5::new(); + md5.update(passwd); + md5.finalize().try_into().unwrap() + }; + + for _ in 1..iteration_count { + let mut md5 = Md5::new(); + md5.update(key); + key = md5.finalize().try_into().unwrap(); + } + Self { - kerbros_password: passwd.into(), + kerbros_password: key.into(), username: username.into(), pid, } From 872f41cab2068cb361283c9ec7951ec5cf513eca Mon Sep 17 00:00:00 2001 From: Maple Nebel Date: Mon, 22 Jun 2026 14:22:53 +0200 Subject: [PATCH 04/29] cleanup --- rnex-core/src/kerberos/mod.rs | 12 +++-------- rnex-core/src/nex/account.rs | 29 ++++++------------------- rnex-core/src/nex/auth_handler.rs | 24 ++++++++++----------- rnex-core/src/nex/user.rs | 36 +++++++++++++++++-------------- rnex-core/src/prudp/ticket.rs | 4 ++-- 5 files changed, 44 insertions(+), 61 deletions(-) diff --git a/rnex-core/src/kerberos/mod.rs b/rnex-core/src/kerberos/mod.rs index 1a44788..c79a1d4 100644 --- a/rnex-core/src/kerberos/mod.rs +++ b/rnex-core/src/kerberos/mod.rs @@ -28,17 +28,11 @@ pub const SESSION_KEY_LENGTH: usize = SessionLengthTy::USIZE; type Md5Hmac = Hmac; -pub fn derive_key(pid: PID, key: &[u8]) -> [u8; 16] { +pub fn derive_kerberos_key(pid: PID, nex_key: [u8; 16]) -> [u8; 16] { let iteration_count = pid % 1024; - // we do one iteration out here to ensure the key is always 16 bytes + let mut key = nex_key; - let mut key: [u8; 16] = { - let mut md5 = Md5::new(); - md5.update(key); - md5.finalize().try_into().unwrap() - }; - - for _ in 1..iteration_count { + for _ in 0..iteration_count { let mut md5 = Md5::new(); md5.update(key); key = md5.finalize().try_into().unwrap(); diff --git a/rnex-core/src/nex/account.rs b/rnex-core/src/nex/account.rs index 08b2cdf..ade79b7 100644 --- a/rnex-core/src/nex/account.rs +++ b/rnex-core/src/nex/account.rs @@ -1,5 +1,5 @@ -use md5::{Digest, Md5}; use macros::RmcSerialize; +use md5::{Digest, Md5}; use rnex_core::PID; @@ -7,7 +7,7 @@ use rnex_core::PID; pub struct Account { pub pid: PID, pub username: String, - pub kerbros_password: Box<[u8]>, + pub nex_key: [u8; 16], } impl Account { @@ -28,36 +28,21 @@ impl Account { } Self { - kerbros_password: key.into(), + nex_key: key.into(), username: username.into(), pid, } } - pub fn new_raw_password(pid: PID, username: &str, passwd: &[u8]) -> Self { - let iteration_count = 65000; - // we do one iteration out here to ensure the key is always 16 bytes - - let mut key: [u8; 16] = { - let mut md5 = Md5::new(); - md5.update(passwd); - md5.finalize().try_into().unwrap() - }; - - for _ in 1..iteration_count { - let mut md5 = Md5::new(); - md5.update(key); - key = md5.finalize().try_into().unwrap(); - } - + pub fn new_raw_key(pid: PID, username: &str, nex_key: [u8; 16]) -> Self { Self { - kerbros_password: key.into(), username: username.into(), pid, + nex_key, } } - pub fn get_login_data(&self) -> (PID, &[u8]) { - (self.pid, &self.kerbros_password) + pub fn get_login_data(&self) -> (PID, [u8; 16]) { + (self.pid, self.nex_key) } } diff --git a/rnex-core/src/nex/auth_handler.rs b/rnex-core/src/nex/auth_handler.rs index 643c228..7c9338b 100644 --- a/rnex-core/src/nex/auth_handler.rs +++ b/rnex-core/src/nex/auth_handler.rs @@ -5,7 +5,7 @@ use cfg_if::cfg_if; use log::{info, warn}; use macros::rmc_struct; use rnex_core::PID; -use rnex_core::kerberos::{KerberosDateTime, Ticket, derive_key}; +use rnex_core::kerberos::{KerberosDateTime, Ticket, derive_kerberos_key}; use rnex_core::nex::account::Account; use rnex_core::rmc::protocols::OnlyRemote; use rnex_core::rmc::protocols::auth::{Auth, RawAuth, RawAuthInfo, RemoteAuth}; @@ -34,11 +34,11 @@ pub struct AuthHandler { } pub fn generate_ticket( - source_act_login_data: (PID, &[u8]), - dest_act_login_data: (PID, &[u8]), + source_act_login_data: (PID, [u8; 16]), + dest_act_login_data: (PID, [u8; 16]), ) -> Box<[u8]> { - let source_key = derive_key(source_act_login_data.0, source_act_login_data.1); - let dest_key = derive_key(dest_act_login_data.0, dest_act_login_data.1); + let source_key = derive_kerberos_key(source_act_login_data.0, source_act_login_data.1); + let dest_key = derive_kerberos_key(dest_act_login_data.0, dest_act_login_data.1); let internal_data = kerberos::TicketInternalData::new(source_act_login_data.0); @@ -53,12 +53,12 @@ pub fn generate_ticket( } pub fn generate_ticket_with_string_user_key( source_act: PID, - dest_act_login_data: (PID, &[u8]), + dest_act_login_data: (PID, [u8; 16]), ) -> (String, Box<[u8]>) { let source_key: [u8; 8] = rand::random(); let key_string = hex::encode(source_key); let key_data: [u8; 16] = key_string.as_bytes().try_into().unwrap(); - let dest_key = derive_key(dest_act_login_data.0, dest_act_login_data.1); + let dest_key = derive_kerberos_key(dest_act_login_data.0, dest_act_login_data.1); let internal_data = kerberos::TicketInternalData::new(source_act); @@ -72,11 +72,11 @@ pub fn generate_ticket_with_string_user_key( (key_string, encrypted_session_ticket) } -async fn get_login_data_by_pid(pid: PID) -> Option<(PID, Box<[u8]>)> { +async fn get_login_data_by_pid(pid: PID) -> Option<(PID, [u8; 16])> { if pid == GUEST_ACCOUNT.pid { let source_login_data = GUEST_ACCOUNT.get_login_data(); - return Some((source_login_data.0, source_login_data.1.into())); + return Some((source_login_data.0, source_login_data.1)); } let Ok(mut client) = account::Client::new().await else { @@ -87,7 +87,7 @@ async fn get_login_data_by_pid(pid: PID) -> Option<(PID, Box<[u8]>)> { return None; }; - Some((pid, passwd.into())) + Some((pid, passwd)) } fn station_url_from_sock_addr(sock_addr: SocketAddrV4) -> String { @@ -133,7 +133,7 @@ impl AuthHandler { return Err(ErrorCode::Core_Exception); }; - let source_login_data = (pid, &passwd[..]); + let source_login_data = (pid, passwd); println!("{}, {:?}", pid, passwd); let destination_login_data = self.destination_server_acct.get_login_data(); @@ -322,7 +322,7 @@ impl Auth for AuthHandler { let result = QResult::success(Core_Unknown); - let ticket = generate_ticket((pid, &passwd[..]), desgination_login_data); + let ticket = generate_ticket((pid, passwd), desgination_login_data); Ok((result, ticket.into())) } diff --git a/rnex-core/src/nex/user.rs b/rnex-core/src/nex/user.rs index d00c5df..8d11329 100644 --- a/rnex-core/src/nex/user.rs +++ b/rnex-core/src/nex/user.rs @@ -39,26 +39,30 @@ use std::str::FromStr; use cfg_if::cfg_if; use log::{error, info}; use macros::rmc_struct; -use rnex_core::prudp::socket_addr::PRUDPSockAddr; -use rnex_core::rmc::protocols::message_delivery::{ - MessageDelivery, RawMessageDelivery, RawMessageDeliveryInfo, RemoteMessageDelivery, +use rnex_core::{ + prudp::socket_addr::PRUDPSockAddr, + rmc::{ + protocols::{ + message_delivery::{MessageDelivery, RemoteMessageDeliveryNoResponse}, + messaging::UserMessage, + notifications::{NotificationEvent, RemoteNotification}, + ranking::{ + CompetitionRankingGetParam, CompetitionRankingScoreData, + CompetitionRankingScoreInfo, + }, + }, + response::ErrorCode::{Core_InvalidArgument, RendezVous_AccountExpired}, + structures::{ + matchmake::{Gathering, MatchmakeSessionSearchCriteria}, + qbuffer::QBuffer, + qresult::QResult, + ranking::UploadCompetitionData, + }, + }, }; -use rnex_core::rmc::protocols::notifications::{NotificationEvent, RemoteNotification}; -use rnex_core::rmc::protocols::ranking::{ - CompetitionRankingGetParam, CompetitionRankingScoreData, CompetitionRankingScoreInfo, -}; -use rnex_core::rmc::response::ErrorCode::{Core_InvalidArgument, RendezVous_AccountExpired}; -use rnex_core::rmc::structures::qbuffer::QBuffer; -use rnex_core::rmc::structures::qresult::QResult; -use rnex_core::rmc::structures::ranking::UploadCompetitionData; use std::sync::{Arc, Weak}; use tokio::sync::{Mutex, RwLock}; -use crate::rmc::protocols::message_delivery::RemoteMessageDeliveryNoResponse; -use crate::rmc::protocols::messaging::UserMessage; -use crate::rmc::structures::matchmake::Gathering; -use crate::rmc::structures::matchmake::MatchmakeSessionSearchCriteria; - cfg_if! { if #[cfg(feature = "datastore")] { use rnex_core::rmc::protocols::datastore::{DataStore, RawDataStore, RawDataStoreInfo, RemoteDataStore}; diff --git a/rnex-core/src/prudp/ticket.rs b/rnex-core/src/prudp/ticket.rs index 97dc5d5..b30c12e 100644 --- a/rnex-core/src/prudp/ticket.rs +++ b/rnex-core/src/prudp/ticket.rs @@ -6,7 +6,7 @@ use typenum::U16; use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; use crate::{ - kerberos::{SESSION_KEY_LENGTH, SessionLengthTy, TicketInternalData, derive_key}, + kerberos::{SESSION_KEY_LENGTH, SessionLengthTy, TicketInternalData, derive_kerberos_key}, nex::account::Account, rmc::structures::RmcSerialize, }; @@ -26,7 +26,7 @@ pub fn read_secure_connection_data( let ticket_data = &mut ticket_data[0..ticket_data_size - 0x10]; - let server_key = derive_key(act.pid, &act.kerbros_password[..]); + let server_key = derive_kerberos_key(act.pid, act.nex_key); let mut rc4: StreamCipherCoreWrapper> = Rc4::new_from_slice(&server_key).expect("unable to init rc4 keystream"); From bb0927c4a080b4bb5f9777f402d84ac07d2c66b7 Mon Sep 17 00:00:00 2001 From: Maple Nebel Date: Mon, 22 Jun 2026 14:30:26 +0200 Subject: [PATCH 05/29] fix smm --- rnex-core/src/nex/user.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/rnex-core/src/nex/user.rs b/rnex-core/src/nex/user.rs index 8d11329..760532b 100644 --- a/rnex-core/src/nex/user.rs +++ b/rnex-core/src/nex/user.rs @@ -43,7 +43,10 @@ use rnex_core::{ prudp::socket_addr::PRUDPSockAddr, rmc::{ protocols::{ - message_delivery::{MessageDelivery, RemoteMessageDeliveryNoResponse}, + message_delivery::{ + MessageDelivery, RawMessageDelivery, RawMessageDeliveryInfo, RemoteMessageDelivery, + RemoteMessageDeliveryNoResponse, + }, messaging::UserMessage, notifications::{NotificationEvent, RemoteNotification}, ranking::{ @@ -88,7 +91,8 @@ cfg_if! { Matchmake, NatTraversal, Utility, - Ranking + Ranking, + MessageDelivery } ); } From 09f8d33e0cc021ea0e5a7c0f22a30ebf48deab58 Mon Sep 17 00:00:00 2001 From: Maple Nebel Date: Mon, 22 Jun 2026 14:55:43 +0200 Subject: [PATCH 06/29] debug logging --- rnex-core/src/grpc/account.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/rnex-core/src/grpc/account.rs b/rnex-core/src/grpc/account.rs index e782d7b..b8c6644 100644 --- a/rnex-core/src/grpc/account.rs +++ b/rnex-core/src/grpc/account.rs @@ -1,15 +1,15 @@ use crate::grpc::account::Error::SomethingHappened; use json::{JsonValue, object}; +use nex_account::grpc::Pid; +use nex_account::grpc::nex_account_service_client::NexAccountServiceClient; use once_cell::sync::Lazy; use rnex_core::PID; use std::array::TryFromSliceError; use std::ops::Deref; -use std::{env, result}; use std::sync::LazyLock; +use std::{env, result}; use thiserror::Error; use tokio::task::{JoinError, spawn_blocking}; -use nex_account::grpc::nex_account_service_client::NexAccountServiceClient; -use nex_account::grpc::Pid; use tonic::transport::Channel; static API_KEY: Lazy = Lazy::new(|| { @@ -52,15 +52,19 @@ pub struct Client(NexAccountServiceClient); //(reqwest::Client); impl Client { pub async fn new() -> Result { - let client = NexAccountServiceClient::connect(NEX_ACCOUNT_URL.as_str()) - .await?; + let client = NexAccountServiceClient::connect(NEX_ACCOUNT_URL.as_str()).await?; Ok(Self(client)) } pub async fn get_nex_key(&mut self, pid: PID) -> Result<[u8; 16]> { - let prekey = self.0.get_nex_key_by_pid(Pid{pid}).await?.into_inner(); + let prekey = self.0.get_nex_key_by_pid(Pid { pid }).await?.into_inner(); - let nexkey: [u8; 16] = prekey.key.try_into().map_err(|_| Error::SomethingHappened)?; + println!("{:?}", prekey); + + let nexkey: [u8; 16] = prekey + .key + .try_into() + .map_err(|_| Error::SomethingHappened)?; Ok(nexkey) } From 0f233fd355c0e8f14bf8f59c0990f7f11f2a192c Mon Sep 17 00:00:00 2001 From: Maple Nebel Date: Mon, 22 Jun 2026 23:06:10 +0200 Subject: [PATCH 07/29] start migrating to new system --- Cargo.lock | 14 +++++++------- rnex-core/Cargo.toml | 2 +- rnex-core/src/kerberos/mod.rs | 13 +------------ rnex-core/src/nex/account.rs | 2 +- rnex-core/src/nex/auth_handler.rs | 8 ++++---- rnex-core/src/nex/friends_handler.rs | 21 +++++++++++++++------ 6 files changed, 29 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 62a64e3..cc00939 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1693,7 +1693,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.3", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -2091,9 +2091,9 @@ checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" [[package]] name = "nex-account" -version = "0.1.3" +version = "0.2.0" source = "sparse+https://crates.spbr.net/api/v1/crates/" -checksum = "16be02914ed26b15f68fc8c0c11f719ca9796e90be5899523baad8a3bdf0069b" +checksum = "10e60c4ce2c2fd11862b85dd0176e0de57b4e9057b78ee98d9b00c82fa978601" dependencies = [ "hmac 0.13.0", "log", @@ -2787,7 +2787,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -3595,10 +3595,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4237,7 +4237,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/rnex-core/Cargo.toml b/rnex-core/Cargo.toml index 1f4e52b..c64f6ba 100644 --- a/rnex-core/Cargo.toml +++ b/rnex-core/Cargo.toml @@ -37,7 +37,7 @@ urlencoding = "2.1.3" futures = "0.3.32" async-trait = "0.1.89" ctor = "1.0.7" -nex-account = { version = "0.1.3", registry = "spbr" } +nex-account = { version = "0.2.0", registry = "spbr" } tonic = "0.14.6" [dev-dependencies] diff --git a/rnex-core/src/kerberos/mod.rs b/rnex-core/src/kerberos/mod.rs index c79a1d4..bada5a6 100644 --- a/rnex-core/src/kerberos/mod.rs +++ b/rnex-core/src/kerberos/mod.rs @@ -3,6 +3,7 @@ use cfg_if::cfg_if; use chrono::{Datelike, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc}; use hmac::Hmac; use hmac::Mac; +use md5::digest::generic_array::GenericArray; use md5::{Digest, Md5}; use rc4::KeyInit; use rc4::cipher::StreamCipherCoreWrapper; @@ -28,18 +29,6 @@ pub const SESSION_KEY_LENGTH: usize = SessionLengthTy::USIZE; type Md5Hmac = Hmac; -pub fn derive_kerberos_key(pid: PID, nex_key: [u8; 16]) -> [u8; 16] { - let iteration_count = pid % 1024; - let mut key = nex_key; - - for _ in 0..iteration_count { - let mut md5 = Md5::new(); - md5.update(key); - key = md5.finalize().try_into().unwrap(); - } - - key -} #[derive(Pod, Zeroable, Copy, Clone, Debug, Eq, PartialEq)] #[repr(transparent)] pub struct KerberosDateTime(pub u64); diff --git a/rnex-core/src/nex/account.rs b/rnex-core/src/nex/account.rs index ade79b7..c5ab04e 100644 --- a/rnex-core/src/nex/account.rs +++ b/rnex-core/src/nex/account.rs @@ -12,7 +12,7 @@ pub struct Account { impl Account { pub fn new(pid: PID, username: &str, passwd: &str) -> Self { - let iteration_count = 65000; + let iteration_count = 65000 + pid % 1024; // we do one iteration out here to ensure the key is always 16 bytes let mut key: [u8; 16] = { diff --git a/rnex-core/src/nex/auth_handler.rs b/rnex-core/src/nex/auth_handler.rs index 7c9338b..d61e8d7 100644 --- a/rnex-core/src/nex/auth_handler.rs +++ b/rnex-core/src/nex/auth_handler.rs @@ -5,7 +5,7 @@ use cfg_if::cfg_if; use log::{info, warn}; use macros::rmc_struct; use rnex_core::PID; -use rnex_core::kerberos::{KerberosDateTime, Ticket, derive_kerberos_key}; +use rnex_core::kerberos::{KerberosDateTime, Ticket}; use rnex_core::nex::account::Account; use rnex_core::rmc::protocols::OnlyRemote; use rnex_core::rmc::protocols::auth::{Auth, RawAuth, RawAuthInfo, RemoteAuth}; @@ -37,8 +37,8 @@ pub fn generate_ticket( source_act_login_data: (PID, [u8; 16]), dest_act_login_data: (PID, [u8; 16]), ) -> Box<[u8]> { - let source_key = derive_kerberos_key(source_act_login_data.0, source_act_login_data.1); - let dest_key = derive_kerberos_key(dest_act_login_data.0, dest_act_login_data.1); + let source_key = source_act_login_data.1; + let dest_key = dest_act_login_data.1; let internal_data = kerberos::TicketInternalData::new(source_act_login_data.0); @@ -58,7 +58,7 @@ pub fn generate_ticket_with_string_user_key( let source_key: [u8; 8] = rand::random(); let key_string = hex::encode(source_key); let key_data: [u8; 16] = key_string.as_bytes().try_into().unwrap(); - let dest_key = derive_kerberos_key(dest_act_login_data.0, dest_act_login_data.1); + let dest_key = dest_act_login_data.1; let internal_data = kerberos::TicketInternalData::new(source_act); diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index 0e09005..5961cf2 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -1,14 +1,17 @@ use std::env; use std::io::{Cursor, Write}; use std::ops::Deref; -use std::sync::{LazyLock, Weak}; use std::sync::{Arc, atomic::AtomicU32}; +use std::sync::{LazyLock, Weak}; use bytemuck::bytes_of; use hmac::Mac; use log::info; use macros::rmc_struct; -use rnex_core::rmc::protocols::account_management::{AccountExtraInfo, AccountManagement, RawAccountManagement, RawAccountManagementInfo, RemoteAccountManagement}; +use rnex_core::rmc::protocols::account_management::{ + AccountExtraInfo, AccountManagement, RawAccountManagement, RawAccountManagementInfo, + RemoteAccountManagement, +}; use rnex_core::rmc::protocols::friends_wiiu::{ FriendsWiiU, RawFriendsWiiU, RawFriendsWiiUInfo, RemoteFriendsWiiU, }; @@ -49,8 +52,8 @@ use rnex_core::rmc::structures::data::Data; use crate::executables::common::get_db; use nex_account::derive_pid_hmac; -use nex_account::grpc::nex_account_service_client::NexAccountServiceClient; use nex_account::grpc::ActCreateInfoNoPid; +use nex_account::grpc::nex_account_service_client::NexAccountServiceClient; define_rmc_proto!( proto FriendsUser{ @@ -293,6 +296,8 @@ impl AccountManagement for FriendsGuest { email: String, auth_data: Any, ) -> Result<(PID, String), ErrorCode> { + todo("fix breaking changes in the code below") + /* println!("{}, {}, {}, {}", principal_name, key, groups, email); if let Ok(data) = auth_data.try_get_as::() { @@ -328,11 +333,15 @@ impl AccountManagement for FriendsGuest { let new_account: ActCreateInfoNoPid = ActCreateInfoNoPid { principal_name, - key: nexkey.into(), email, + }; - let pid = client.create_new_sequential_or_update_and_get_account(new_account).await.map_err(|_| ErrorCode::Core_Unknown)?.into_inner(); + let pid = client + .create_new_sequential_or_update_and_get_account(new_account) + .await + .map_err(|_| ErrorCode::Core_Unknown)? + .into_inner(); let mac = derive_pid_hmac(pid.pid, &nexkey); @@ -341,6 +350,6 @@ impl AccountManagement for FriendsGuest { return Ok((pid.pid, hex_str)); } - Err(ErrorCode::Authentication_InvalidParam) + Err(ErrorCode::Authentication_InvalidParam)*/ } } From ba31ba9a458136f8f92c9f6f3f7347f0c194f0c1 Mon Sep 17 00:00:00 2001 From: Maple Nebel Date: Mon, 22 Jun 2026 23:21:10 +0200 Subject: [PATCH 08/29] fix a few remaining issues --- rnex-core/src/prudp/ticket.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rnex-core/src/prudp/ticket.rs b/rnex-core/src/prudp/ticket.rs index b30c12e..35a4708 100644 --- a/rnex-core/src/prudp/ticket.rs +++ b/rnex-core/src/prudp/ticket.rs @@ -6,7 +6,7 @@ use typenum::U16; use v_byte_helpers::{IS_BIG_ENDIAN, ReadExtensions}; use crate::{ - kerberos::{SESSION_KEY_LENGTH, SessionLengthTy, TicketInternalData, derive_kerberos_key}, + kerberos::{SESSION_KEY_LENGTH, SessionLengthTy, TicketInternalData}, nex::account::Account, rmc::structures::RmcSerialize, }; @@ -26,7 +26,7 @@ pub fn read_secure_connection_data( let ticket_data = &mut ticket_data[0..ticket_data_size - 0x10]; - let server_key = derive_kerberos_key(act.pid, act.nex_key); + let server_key = act.nex_key; let mut rc4: StreamCipherCoreWrapper> = Rc4::new_from_slice(&server_key).expect("unable to init rc4 keystream"); From baf6c548f7d611d9117715e7d8adb21a64111886 Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 23 Jun 2026 00:56:23 +0200 Subject: [PATCH 09/29] update nintendocreateaccount --- rnex-core/src/nex/friends_handler.rs | 61 +++++++++++++++++++++------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index 5961cf2..f30ccef 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -4,7 +4,8 @@ use std::ops::Deref; use std::sync::{Arc, atomic::AtomicU32}; use std::sync::{LazyLock, Weak}; -use bytemuck::bytes_of; +use base64::{engine::general_purpose, Engine as _}; +use bytemuck::{bytes_of, Pod, Zeroable}; use hmac::Mac; use log::info; use macros::rmc_struct; @@ -36,6 +37,7 @@ use rnex_core::{ }; use sqlx::query; use std::sync::atomic::Ordering::Relaxed; +use hex::decode; use tokio::spawn; use tokio::sync::RwLock; @@ -52,7 +54,7 @@ use rnex_core::rmc::structures::data::Data; use crate::executables::common::get_db; use nex_account::derive_pid_hmac; -use nex_account::grpc::ActCreateInfoNoPid; +use nex_account::grpc::ActCreateInfo; use nex_account::grpc::nex_account_service_client::NexAccountServiceClient; define_rmc_proto!( @@ -81,6 +83,15 @@ pub struct UserData { presence: NintendoPresenceV2, } +#[repr(C, packed)] +#[derive(Pod, Zeroable, Copy, Clone)] +pub struct NascToken { + pub pid: i32, + pub time: [u8; 14], + pub pwd_hash: [u8; 4], +} + + #[rmc_struct(FriendsUser)] pub struct FriendsUser { pub fm: Arc, @@ -287,6 +298,20 @@ impl Secure for FriendsGuest { } } +fn decode_token(encoded_str: &str) -> Result { + let bytes = general_purpose::STANDARD + .decode(encoded_str) + .map_err(|_| "failed to decode Base64 string")?; + + if bytes.len() != std::mem::size_of::() { + return Err("decoded byte length mismatch"); + } + + let token = bytemuck::from_bytes::(&bytes); + + Ok(*token) +} + impl AccountManagement for FriendsGuest { async fn nintendo_create_account( &self, @@ -296,8 +321,6 @@ impl AccountManagement for FriendsGuest { email: String, auth_data: Any, ) -> Result<(PID, String), ErrorCode> { - todo("fix breaking changes in the code below") - /* println!("{}, {}, {}, {}", principal_name, key, groups, email); if let Ok(data) = auth_data.try_get_as::() { @@ -318,6 +341,9 @@ impl AccountManagement for FriendsGuest { if let Ok(extra_info) = auth_data.try_get_as::() { info!("create account via extra info"); + + let decoded_token = decode_token(&*extra_info.nex_token).map_err(|_| ErrorCode::Authentication_InvalidParam)?; + let mut client = NexAccountServiceClient::connect(NEX_ACCOUNT_URL.as_str()) .await .map_err(|e| { @@ -325,31 +351,36 @@ impl AccountManagement for FriendsGuest { ErrorCode::Core_Unknown })?; - let nexkey: [u8; 16] = hex::decode(key) - .map_err(|_| ErrorCode::Authentication_InvalidParam)? - .as_slice() - .try_into() - .map_err(|_| ErrorCode::Authentication_InvalidParam)?; + let pid = decoded_token.pid; - let new_account: ActCreateInfoNoPid = ActCreateInfoNoPid { + let new_account: ActCreateInfo = ActCreateInfo { principal_name, + key: vec![], email, - + pid: 0, }; - let pid = client + let nexkey = client .create_new_sequential_or_update_and_get_account(new_account) .await .map_err(|_| ErrorCode::Core_Unknown)? .into_inner(); - let mac = derive_pid_hmac(pid.pid, &nexkey); + if nexkey.key.len() != 16 { + log::error!("nex key was not 16 bytes long"); + return Err(ErrorCode::Authentication_InvalidParam); + } + + let nexkeyarray: [u8; 16] = nexkey.key.try_into() + .expect("how...?"); + + let mac = derive_pid_hmac(pid, &nexkeyarray); let hex_str = hex::encode(mac); - return Ok((pid.pid, hex_str)); + return Ok((pid, hex_str)); } - Err(ErrorCode::Authentication_InvalidParam)*/ + Err(ErrorCode::Authentication_InvalidParam) } } From 73d70a618847c55e43cdc38ac82f0ead9df2dab0 Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 23 Jun 2026 01:16:43 +0200 Subject: [PATCH 10/29] debugging --- rnex-core/src/nex/friends_handler.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index f30ccef..6608b97 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -342,7 +342,10 @@ impl AccountManagement for FriendsGuest { if let Ok(extra_info) = auth_data.try_get_as::() { info!("create account via extra info"); - let decoded_token = decode_token(&*extra_info.nex_token).map_err(|_| ErrorCode::Authentication_InvalidParam)?; + let decoded_token = decode_token(&*extra_info.nex_token).map_err(|e| { + log::error!("failed to decode token: {}", e); + ErrorCode::Authentication_InvalidParam + })?; let mut client = NexAccountServiceClient::connect(NEX_ACCOUNT_URL.as_str()) .await From ca2e7372edbd8d4dfeca9e8c824cf2262da37e18 Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 23 Jun 2026 01:19:21 +0200 Subject: [PATCH 11/29] please show me the issue --- rnex-core/src/nex/friends_handler.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index 6608b97..1fc8e73 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -344,6 +344,7 @@ impl AccountManagement for FriendsGuest { let decoded_token = decode_token(&*extra_info.nex_token).map_err(|e| { log::error!("failed to decode token: {}", e); + log::info!("{:?}", extra_info.nex_token); ErrorCode::Authentication_InvalidParam })?; From 6f0e6c4516ccd3e9476a73969f86fb5df3d87c90 Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 23 Jun 2026 01:27:22 +0200 Subject: [PATCH 12/29] this should be the last fix --- rnex-core/src/nex/friends_handler.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index 1fc8e73..c5a222d 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -84,7 +84,7 @@ pub struct UserData { } #[repr(C, packed)] -#[derive(Pod, Zeroable, Copy, Clone)] +#[derive(Pod, Zeroable, Copy, Clone, Debug)] pub struct NascToken { pub pid: i32, pub time: [u8; 14], @@ -298,9 +298,18 @@ impl Secure for FriendsGuest { } } + +// this is probably a horrible way to do this. it's 1:27am. fuck off please i'll fix it later. fn decode_token(encoded_str: &str) -> Result { + // i dont like the use of replace here as this will reallocate the entire string + // im gonna keep it though as long as noone comes up with a good alternative for this + // without complicating the code/making it far less readable/unnescesarily long + let standard_b64 = encoded_str.replace('.', "+").replace('-', "/").replace('*', "="); + + let standard = general_purpose::STANDARD.decode(standard_b64).map_err(|_| "failed to convert")?; + let bytes = general_purpose::STANDARD - .decode(encoded_str) + .decode(standard) .map_err(|_| "failed to decode Base64 string")?; if bytes.len() != std::mem::size_of::() { @@ -348,6 +357,8 @@ impl AccountManagement for FriendsGuest { ErrorCode::Authentication_InvalidParam })?; + log::info!("decoded token: {:?}", decoded_token); + let mut client = NexAccountServiceClient::connect(NEX_ACCOUNT_URL.as_str()) .await .map_err(|e| { From 6e52e282a16d72fc2e3172a95f67c017663e84ad Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 23 Jun 2026 01:32:31 +0200 Subject: [PATCH 13/29] guess who hardcoded the pid to 0 on accident? yours truly! --- rnex-core/src/nex/friends_handler.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index c5a222d..1b83c09 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -372,13 +372,13 @@ impl AccountManagement for FriendsGuest { principal_name, key: vec![], email, - pid: 0, + pid, }; let nexkey = client .create_new_sequential_or_update_and_get_account(new_account) .await - .map_err(|_| ErrorCode::Core_Unknown)? + .map_err(|e| {ErrorCode::Core_Unknown})? .into_inner(); if nexkey.key.len() != 16 { From c172b3eb8e0f849a3909e42b85b60f2c2fe23805 Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 23 Jun 2026 01:38:50 +0200 Subject: [PATCH 14/29] is it empty? --- rnex-core/src/grpc/account.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rnex-core/src/grpc/account.rs b/rnex-core/src/grpc/account.rs index b8c6644..3bd9445 100644 --- a/rnex-core/src/grpc/account.rs +++ b/rnex-core/src/grpc/account.rs @@ -59,7 +59,7 @@ impl Client { pub async fn get_nex_key(&mut self, pid: PID) -> Result<[u8; 16]> { let prekey = self.0.get_nex_key_by_pid(Pid { pid }).await?.into_inner(); - println!("{:?}", prekey); + log::warn!("prekey is {:?}", prekey); let nexkey: [u8; 16] = prekey .key From 49fd714d07ef03b58dba9d41299e0763594601a1 Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 23 Jun 2026 01:42:08 +0200 Subject: [PATCH 15/29] more debugging to help --- rnex-core/src/nex/auth_handler.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rnex-core/src/nex/auth_handler.rs b/rnex-core/src/nex/auth_handler.rs index d61e8d7..2ba5486 100644 --- a/rnex-core/src/nex/auth_handler.rs +++ b/rnex-core/src/nex/auth_handler.rs @@ -109,6 +109,7 @@ impl AuthHandler { #[cfg(feature = "guest_login")] { if name == GUEST_ACCOUNT.username { + log::info!("guest account login"); let source_login_data = GUEST_ACCOUNT.get_login_data(); let destination_login_data = self.destination_server_acct.get_login_data(); @@ -118,25 +119,31 @@ impl AuthHandler { )); } } + + log::info!("parsing pid"); let Ok(pid) = name.parse() else { warn!("unable to connect to parse pid: {}", name); return Err(ErrorCode::Core_InvalidArgument); }; + log::info!("creating account grpc client"); let Ok(mut client) = account::Client::new().await else { warn!("unable to connect to grpc"); return Err(ErrorCode::Core_Exception); }; + log::info!("grabbing nex key"); let Ok(passwd) = client.get_nex_key(pid).await else { warn!("unable to get nex password for pid: {}:", pid); return Err(ErrorCode::Core_Exception); }; + log::info!("source login data"); let source_login_data = (pid, passwd); println!("{}, {:?}", pid, passwd); let destination_login_data = self.destination_server_acct.get_login_data(); + log::info!("we are a-ok here"); Ok(( pid, generate_ticket(source_login_data, destination_login_data), From 929516da5a33595f943f00b0229f1750a49b8119 Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 23 Jun 2026 12:10:55 +0200 Subject: [PATCH 16/29] placeholder for Friends3DS proto --- Cargo.lock | 10 +-- rnex-core/Cargo.toml | 2 +- rnex-core/src/nex/friends_handler.rs | 115 ++++++++++++++++++++++++++- 3 files changed, 120 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc00939..9ad2257 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1693,7 +1693,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.3", "tokio", "tower-service", "tracing", @@ -2091,9 +2091,9 @@ checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" [[package]] name = "nex-account" -version = "0.2.0" +version = "0.2.1" source = "sparse+https://crates.spbr.net/api/v1/crates/" -checksum = "10e60c4ce2c2fd11862b85dd0176e0de57b4e9057b78ee98d9b00c82fa978601" +checksum = "51215ae67dfb36017956435ee4935bd62d64b62a8dd716c4614e11c8c29f941e" dependencies = [ "hmac 0.13.0", "log", @@ -3595,7 +3595,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.3.4", "once_cell", "rustix", "windows-sys 0.61.2", @@ -4237,7 +4237,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/rnex-core/Cargo.toml b/rnex-core/Cargo.toml index c64f6ba..66c2fc3 100644 --- a/rnex-core/Cargo.toml +++ b/rnex-core/Cargo.toml @@ -37,7 +37,7 @@ urlencoding = "2.1.3" futures = "0.3.32" async-trait = "0.1.89" ctor = "1.0.7" -nex-account = { version = "0.2.0", registry = "spbr" } +nex-account = { version = "0.2.1", registry = "spbr" } tonic = "0.14.6" [dev-dependencies] diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index 1b83c09..94c9bda 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -16,6 +16,9 @@ use rnex_core::rmc::protocols::account_management::{ use rnex_core::rmc::protocols::friends_wiiu::{ FriendsWiiU, RawFriendsWiiU, RawFriendsWiiUInfo, RemoteFriendsWiiU, }; +use rnex_core::rmc::protocols::friends_3ds::{ + Friends3DS, RawFriends3DS, RawFriends3DSInfo, RemoteFriends3DS, +}; use rnex_core::rmc::protocols::nintendo_notification::{ NintendoNotification, RawNintendoNotification, RawNintendoNotificationInfo, RemoteNintendoNotification, @@ -56,11 +59,13 @@ use crate::executables::common::get_db; use nex_account::derive_pid_hmac; use nex_account::grpc::ActCreateInfo; use nex_account::grpc::nex_account_service_client::NexAccountServiceClient; +use crate::rmc::protocols::friends_3ds::{FriendComment, FriendMii, FriendMiiList, FriendPersistentInfo, FriendPicture, FriendPresence, FriendRelationship, Mii, MiiList, MyProfile, NintendoPresence, PlayedGame}; define_rmc_proto!( proto FriendsUser{ Secure, - FriendsWiiU + FriendsWiiU, + Friends3DS } ); define_rmc_proto!( @@ -120,6 +125,114 @@ impl FriendsManager { } } +// ALL of this is stubbed +impl Friends3DS for FriendsUser { + async fn update_profile(&self, profile: MyProfile) -> Result<(), ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } + + async fn update_mii(&self, profile: Mii) -> Result<(), ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } + + async fn update_mii_list(&self, profile: MiiList) -> Result<(), ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } + + async fn update_played_games(&self, profile: Vec) -> Result<(), ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } + + async fn update_preference(&self, show_online_status: bool, show_current_title: bool, block_friend_requests: bool) -> Result<(), ErrorCode> { + // stubbed + Ok(()) + } + + async fn get_friend_mii(&self, friends: Vec) -> Result, ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } + + async fn get_friend_mii_list(&self, friends: Vec) -> Result, ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } + + async fn is_active_game(&self, unk: Vec, game_key: crate::rmc::protocols::friends_3ds::GameKey) -> Result, ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } + + async fn get_principal_id_by_local_friend_code(&self, unk1: u64, unk2: Vec) -> Result, ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } + + async fn get_friend_relationships(&self, unk2: Vec) -> Result, ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } + + async fn add_friend_by_pid(&self, unk: u64, pid: PID) -> Result { + Err(ErrorCode::Core_NotImplemented) + } + + async fn add_friend_by_lst_pid(&self, unk: u64, pid: Vec) -> Result, ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } + + async fn remove_friend_by_local_code(&self, local_code: u64) -> Result<(), ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } + + async fn remove_friend_by_pid(&self, pid: PID) -> Result<(), ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } + + async fn get_all_friends(&self) -> Result, ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } + + async fn update_blacklist(&self) -> Result<(), ErrorCode> { + Ok(()) + } + + async fn sync_friend(&self, unk1: u64, unk2: Vec, unk3: Vec) -> Result, ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } + + async fn update_presence(&self, nintendo_presence: NintendoPresence, unk: bool) -> Result<(), ErrorCode> { + Ok(()) + } + + async fn update_favorite_game_key(&self, game_key: crate::rmc::protocols::friends_3ds::GameKey) -> Result<(), ErrorCode> { + Ok(()) + } + + async fn update_comment(&self, comment: String) -> Result<(), ErrorCode> { + Ok(()) + } + + async fn update_picture(&self, unk: u32, picture: Vec) -> Result<(), ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } + + async fn get_friend_presence(&self, unk: Vec) -> Result, ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } + + async fn get_friend_comment(&self, unk: Vec) -> Result, ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } + + async fn get_friend_picture(&self, unk: Vec) -> Result, ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } + + async fn get_friend_persistent_info(&self, unk: Vec) -> Result, ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } + + async fn send_invitation(&self, unk: Vec) -> Result<(), ErrorCode> { + Err(ErrorCode::Core_NotImplemented) + } +} + impl FriendsWiiU for FriendsUser { async fn update_and_get_all_information( &self, From 54d0339f48ad03c3635aa3a0c1b607ac0fd89314 Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 23 Jun 2026 12:14:03 +0200 Subject: [PATCH 17/29] stub more stuff the 3DS requests --- rnex-core/src/nex/friends_handler.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index 94c9bda..3979d2c 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -128,7 +128,7 @@ impl FriendsManager { // ALL of this is stubbed impl Friends3DS for FriendsUser { async fn update_profile(&self, profile: MyProfile) -> Result<(), ErrorCode> { - Err(ErrorCode::Core_NotImplemented) + Ok(()) } async fn update_mii(&self, profile: Mii) -> Result<(), ErrorCode> { @@ -140,7 +140,7 @@ impl Friends3DS for FriendsUser { } async fn update_played_games(&self, profile: Vec) -> Result<(), ErrorCode> { - Err(ErrorCode::Core_NotImplemented) + Ok(()) } async fn update_preference(&self, show_online_status: bool, show_current_title: bool, block_friend_requests: bool) -> Result<(), ErrorCode> { From 8584136c7145cce6cb0b41bb9396f8409645ef36 Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 23 Jun 2026 12:15:47 +0200 Subject: [PATCH 18/29] yeah it needed more stuff --- rnex-core/src/nex/friends_handler.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index 3979d2c..8c9c633 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -132,11 +132,11 @@ impl Friends3DS for FriendsUser { } async fn update_mii(&self, profile: Mii) -> Result<(), ErrorCode> { - Err(ErrorCode::Core_NotImplemented) + Ok(()) } async fn update_mii_list(&self, profile: MiiList) -> Result<(), ErrorCode> { - Err(ErrorCode::Core_NotImplemented) + Ok(()) } async fn update_played_games(&self, profile: Vec) -> Result<(), ErrorCode> { From be9898d3af8c28d3e7ad149d20b3ef9f8bc46162 Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 23 Jun 2026 12:40:52 +0200 Subject: [PATCH 19/29] stub SyncFriend --- rnex-core/src/nex/friends_handler.rs | 2 +- rnex-core/src/rmc/protocols/friends_3ds.rs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index 8c9c633..d17d64c 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -193,7 +193,7 @@ impl Friends3DS for FriendsUser { } async fn sync_friend(&self, unk1: u64, unk2: Vec, unk3: Vec) -> Result, ErrorCode> { - Err(ErrorCode::Core_NotImplemented) + Ok(vec![]) } async fn update_presence(&self, nintendo_presence: NintendoPresence, unk: bool) -> Result<(), ErrorCode> { diff --git a/rnex-core/src/rmc/protocols/friends_3ds.rs b/rnex-core/src/rmc/protocols/friends_3ds.rs index aaa7af0..1945764 100644 --- a/rnex-core/src/rmc/protocols/friends_3ds.rs +++ b/rnex-core/src/rmc/protocols/friends_3ds.rs @@ -24,9 +24,9 @@ pub struct MyProfile { pub struct Mii { #[extends] pub data: Data, - pub unk1: String, - pub unk2: bool, - pub unk3: u8, + pub name: String, + pub profanity: bool, + pub char_set: u8, // 0 is JPN/USA/EUR, 1 is CHN, 2 is KOR and 3 is TWN pub mii_data: Vec, } @@ -62,7 +62,7 @@ pub struct PlayedGame { #[derive(RmcSerialize)] #[rmc_struct(0)] pub struct FriendInfo { - pub unk1: u32, + pub pid: u32, pub unk2: KerberosDateTime, } @@ -91,9 +91,9 @@ pub struct FriendMiiList { pub struct FriendRelationship { #[extends] pub data: Data, - pub unk1: u32, - pub unk2: u64, - pub unk3: u8, + pub pid: u32, + pub local_friend_code: u64, + pub relationship_type: u8, } #[derive(RmcSerialize)] @@ -117,7 +117,7 @@ pub struct NintendoPresence { pub struct FriendPresence { #[extends] pub data: Data, - pub unk: u32, + pub pid: u32, pub presence: NintendoPresence, } #[derive(RmcSerialize)] From 2a16f6df55194ed70b77a12f339f730ac5901f01 Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 23 Jun 2026 13:26:41 +0200 Subject: [PATCH 20/29] implement CTRPickupCourseSearchObject (identical to RecommendedCourseSearchObject) --- rnex-core/src/nex/datastore.rs | 108 +++++++++++++++++++++++ rnex-core/src/rmc/protocols/datastore.rs | 6 ++ 2 files changed, 114 insertions(+) diff --git a/rnex-core/src/nex/datastore.rs b/rnex-core/src/nex/datastore.rs index 20e2acd..7d0d7cd 100644 --- a/rnex-core/src/nex/datastore.rs +++ b/rnex-core/src/nex/datastore.rs @@ -1810,4 +1810,112 @@ impl DataStore for User { Ok((ranking_results, q_results)) } + + async fn ctr_pickup_course_search_object(&self, course_search_param: DataStoreSearchParam, extra_data: Vec) -> Result, ErrorCode> { + let mut courses = Vec::new(); + + let mut stream = sqlx::query!( + r#" + SELECT + object.data_id, + object.owner, + object.size, + object.name, + object.data_type, + object.meta_binary, + object.permission, + object.permission_recipients, + object.delete_permission, + object.delete_permission_recipients, + object.period, + object.refer_data_id, + object.flag, + object.tags, + object.creation_date, + object.update_date, + ranking.value + FROM ( + SELECT * FROM datastore.objects object + WHERE + object.upload_completed = TRUE AND + object.deleted = FALSE AND + object.under_review = FALSE + ) object + JOIN ( + SELECT data_id, value + FROM datastore.object_custom_rankings ranking + WHERE ranking.application_id = 0 + ) ranking + ON + object.data_id = ranking.data_id + ORDER BY RANDOM() + LIMIT 100 + "# + ) + .fetch(get_db()); + + while let Some(row) = stream.try_next().await.map_err(|e| { + eprintln!("stream error: {:?}", e); + ErrorCode::DataStore_NotFound + })? { + let permission = Permission { + permission: row.permission.unwrap_or(0) as u8, + recipient_ids: row.permission_recipients.unwrap_or_default(), + }; + + let del_permission = Permission { + permission: row.delete_permission.unwrap_or(0) as u8, + recipient_ids: row.delete_permission_recipients.unwrap_or_default(), + }; + + let meta_binary = row.meta_binary.map(QBuffer).unwrap_or_default(); + + let created_time = row + .creation_date + .map(KerberosDateTime::from_naive) + .unwrap_or_default(); + + let updated_time = row + .update_date + .map(KerberosDateTime::from_naive) + .unwrap_or_default(); + + let referred_time = row + .creation_date + .map(KerberosDateTime::from_naive) + .unwrap_or_default(); + + let mut meta_info = GetMetaInfo { + dataid: row.data_id, + owner: row.owner.unwrap_or(0), + size: row.size.unwrap_or(0) as u32, + name: row.name, + data_type: row.data_type.unwrap_or(0) as u16, + meta_binary, + permission, + del_permission, + period: row.period.unwrap_or(0) as u16, + status: 0, + referred_count: 0, + refer_dat_id: row.refer_data_id.unwrap_or(0) as u32, + flag: row.flag.unwrap_or(0) as u32, + tags: row.tags.unwrap_or_default(), + expire_time: KerberosDateTime::PRACTICALLY_NEVER, + created_time, + updated_time, + referred_time, + ratings: get_rating_with_slot_data_id(row.data_id).await?, + }; + + let course = DataStoreCustomRankingResult { + order: 0, + score: row.value.unwrap_or(0) as u32, + meta_info, + }; + + courses.push(course); + } + + Ok(courses) + } } diff --git a/rnex-core/src/rmc/protocols/datastore.rs b/rnex-core/src/rmc/protocols/datastore.rs index a30b857..e6b847d 100644 --- a/rnex-core/src/rmc/protocols/datastore.rs +++ b/rnex-core/src/rmc/protocols/datastore.rs @@ -453,6 +453,12 @@ pub trait DataStore { &self, application_id: u32, ) -> Result; + #[method_id(82)] + async fn ctr_pickup_course_search_object( + &self, + course_search_param: DataStoreSearchParam, + extra_data: Vec, + ) -> Result, ErrorCode>; #[method_id(87)] async fn report_course( &self, From dfc12ca3d823773a8aa282c2aecedf501fec5e57 Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 23 Jun 2026 14:39:42 +0200 Subject: [PATCH 21/29] preliminary ACNL support --- editions.yaml | 12 ++++++++++++ rnex-core/src/nex/user.rs | 9 +++++++++ rnex-core/src/rmc/protocols/util.rs | 2 ++ 3 files changed, 23 insertions(+) diff --git a/editions.yaml b/editions.yaml index 38bd5ba..df2ef8a 100644 --- a/editions.yaml +++ b/editions.yaml @@ -9,6 +9,18 @@ sonic-transformed: RNEX_VIRTUAL_PORT_SECURE: "1:10" RNEX_DEFAULT_PORT: 10000 RNEX_ACCESS_KEY: "b26a3421" +ac-new-leaf: + include-in-checkall: true + features: + - prudpv1 + - datastore + - v3-10-22 + settings: + AUTH_REPORT_VERSION: "branch:origin/release/ngs/3.10.x.200x build:3_10_22_2006_0" + RNEX_VIRTUAL_PORT_INSECURE: "1:10" + RNEX_VIRTUAL_PORT_SECURE: "1:10" + RNEX_DEFAULT_PORT: 10000 + RNEX_ACCESS_KEY: "d6f08b40" wii-sports-club: include-in-checkall: true features: diff --git a/rnex-core/src/nex/user.rs b/rnex-core/src/nex/user.rs index 760532b..029b856 100644 --- a/rnex-core/src/nex/user.rs +++ b/rnex-core/src/nex/user.rs @@ -813,6 +813,15 @@ impl Utility for User { async fn acquire_nex_unique_id(&self) -> Result { return Ok(rand::random()); } + + async fn get_integer_settings(&self, index: u32) -> Result, ErrorCode> { + Ok(vec![ + (0, 1), + (1, 2), + (2, 0), + (3, 4) + ]) + } } impl Ranking for User { diff --git a/rnex-core/src/rmc/protocols/util.rs b/rnex-core/src/rmc/protocols/util.rs index a89c79b..3d0c2c1 100644 --- a/rnex-core/src/rmc/protocols/util.rs +++ b/rnex-core/src/rmc/protocols/util.rs @@ -6,4 +6,6 @@ use rnex_core::rmc::response::ErrorCode; pub trait Utility { #[method_id(1)] async fn acquire_nex_unique_id(&self) -> Result; + #[method_id(7)] + async fn get_integer_settings(&self, index: u32) -> Result, ErrorCode>; } From 58c25d791e234165159b8eff960b0b2d525d0c76 Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 23 Jun 2026 15:21:55 +0200 Subject: [PATCH 22/29] try to stub friends --- rnex-core/src/nex/friends_handler.rs | 103 +++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 6 deletions(-) diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index d17d64c..74105da 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -149,7 +149,36 @@ impl Friends3DS for FriendsUser { } async fn get_friend_mii(&self, friends: Vec) -> Result, ErrorCode> { - Err(ErrorCode::Core_NotImplemented) + // sorry for the copying pretendo but i don't have a mii on hand rn + let data: Vec = vec![ + 0x03, 0x00, 0x00, 0x40, 0xE9, 0x55, 0xA2, 0x09, + 0xE7, 0xC7, 0x41, 0x82, 0xD9, 0x7D, 0x0B, 0x2D, + 0x03, 0xB3, 0xB8, 0x8D, 0x27, 0xD9, 0x00, 0x00, + 0x01, 0x40, 0x62, 0x00, 0x65, 0x00, 0x6C, 0x00, + 0x6C, 0x00, 0x61, 0x00, 0x00, 0x00, 0x45, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, + 0x12, 0x00, 0x81, 0x01, 0x04, 0x68, 0x43, 0x18, + 0x20, 0x34, 0x46, 0x14, 0x81, 0x12, 0x17, 0x68, + 0x0D, 0x00, 0x00, 0x29, 0x03, 0x52, 0x48, 0x50, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x86, + ]; + + let dummymii = FriendMii { + data: Data {}, + pid: 0, + mii: Mii { + data: Data {}, + name: "test".to_string(), + profanity: false, + char_set: 0, + mii_data: data, + }, + modified_at: Default::default(), + }; + + Ok(vec![dummymii]) } async fn get_friend_mii_list(&self, friends: Vec) -> Result, ErrorCode> { @@ -165,7 +194,14 @@ impl Friends3DS for FriendsUser { } async fn get_friend_relationships(&self, unk2: Vec) -> Result, ErrorCode> { - Err(ErrorCode::Core_NotImplemented) + let dummy = FriendRelationship { + data: Data {}, + pid: 69, + local_friend_code: 1, + relationship_type: 1, + }; + + Ok(vec![dummy]) } async fn add_friend_by_pid(&self, unk: u64, pid: PID) -> Result { @@ -185,7 +221,14 @@ impl Friends3DS for FriendsUser { } async fn get_all_friends(&self) -> Result, ErrorCode> { - Err(ErrorCode::Core_NotImplemented) + let dummy = FriendRelationship { + data: Data {}, + pid: 69, + local_friend_code: 1, + relationship_type: 1, + }; + + Ok(vec![dummy]) } async fn update_blacklist(&self) -> Result<(), ErrorCode> { @@ -193,7 +236,14 @@ impl Friends3DS for FriendsUser { } async fn sync_friend(&self, unk1: u64, unk2: Vec, unk3: Vec) -> Result, ErrorCode> { - Ok(vec![]) + let dummy = FriendRelationship { + data: Data {}, + pid: 69, + local_friend_code: 1, + relationship_type: 1, + }; + + Ok(vec![dummy]) } async fn update_presence(&self, nintendo_presence: NintendoPresence, unk: bool) -> Result<(), ErrorCode> { @@ -213,7 +263,29 @@ impl Friends3DS for FriendsUser { } async fn get_friend_presence(&self, unk: Vec) -> Result, ErrorCode> { - Err(ErrorCode::Core_NotImplemented) + let presence = FriendPresence { + data: Data {}, + pid: 69, + presence: NintendoPresence { + data: Data {}, + changed_bit_flag: 0, + game_key: rnex_core::rmc::protocols::friends_3ds::GameKey { + data: Data {}, + title_id: 0x0005000010176900, + version: 0, + }, + game_mode_desctiption: "".to_string(), + join_availibility_flag: 0, + mm_system_type: 0, + join_game_id: 0, + join_game_mode: 0, + owner_pid: 0, + join_group_id: 0, + application_arg: vec![], + }, + }; + + Ok(vec![]) } async fn get_friend_comment(&self, unk: Vec) -> Result, ErrorCode> { @@ -225,7 +297,26 @@ impl Friends3DS for FriendsUser { } async fn get_friend_persistent_info(&self, unk: Vec) -> Result, ErrorCode> { - Err(ErrorCode::Core_NotImplemented) + let dummypersistentinfo = FriendPersistentInfo { + data: Data {}, + pid: 69, + region: 0, + country: 0, + area: 0, + language: 0, + platform: 0, + game_key: rnex_core::rmc::protocols::friends_3ds::GameKey { + data: Data {}, + title_id: 0x0005000010176900, + version: 0, + }, + message: "yo whats up".to_string(), + msg_updated_at: KerberosDateTime::now(), + friended_at: KerberosDateTime::now(), + last_online: KerberosDateTime::now(), + }; + + Ok(vec![dummypersistentinfo]) } async fn send_invitation(&self, unk: Vec) -> Result<(), ErrorCode> { From 535f05d5c5b05541bbd7ab7ca8ee0417f4527e09 Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 23 Jun 2026 15:22:20 +0200 Subject: [PATCH 23/29] oops --- rnex-core/src/nex/friends_handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index 74105da..531b09e 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -285,7 +285,7 @@ impl Friends3DS for FriendsUser { }, }; - Ok(vec![]) + Ok(vec![presence]) } async fn get_friend_comment(&self, unk: Vec) -> Result, ErrorCode> { From f0fe18ebcb5fe7f076c52df691cad9a468bb6886 Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 23 Jun 2026 15:25:29 +0200 Subject: [PATCH 24/29] forgot to set pid to 69 --- rnex-core/src/nex/friends_handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index 531b09e..2038511 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -167,7 +167,7 @@ impl Friends3DS for FriendsUser { let dummymii = FriendMii { data: Data {}, - pid: 0, + pid: 69, mii: Mii { data: Data {}, name: "test".to_string(), From a28308ef0b34ddcb94e85881799fb995c42caec5 Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 23 Jun 2026 15:32:18 +0200 Subject: [PATCH 25/29] logging --- rnex-core/src/nex/friends_handler.rs | 6 +++++- rnex-core/src/rmc/protocols/friends_3ds.rs | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index 2038511..a7f39d3 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -236,6 +236,8 @@ impl Friends3DS for FriendsUser { } async fn sync_friend(&self, unk1: u64, unk2: Vec, unk3: Vec) -> Result, ErrorCode> { + log::info!("params: {:?}, {:?}, {:?}", unk1, unk2, unk3); + let dummy = FriendRelationship { data: Data {}, pid: 69, @@ -250,7 +252,9 @@ impl Friends3DS for FriendsUser { Ok(()) } - async fn update_favorite_game_key(&self, game_key: crate::rmc::protocols::friends_3ds::GameKey) -> Result<(), ErrorCode> { + async fn update_favorite_game_key(&self, game_key: rnex_core::rmc::protocols::friends_3ds::GameKey) -> Result<(), ErrorCode> { + log::info!("favorite game key: {:?}", game_key); + Ok(()) } diff --git a/rnex-core/src/rmc/protocols/friends_3ds.rs b/rnex-core/src/rmc/protocols/friends_3ds.rs index 1945764..ec2bec6 100644 --- a/rnex-core/src/rmc/protocols/friends_3ds.rs +++ b/rnex-core/src/rmc/protocols/friends_3ds.rs @@ -41,7 +41,7 @@ pub struct MiiList { pub mii_data: Vec>, } -#[derive(RmcSerialize)] +#[derive(RmcSerialize, Debug)] #[rmc_struct(0)] pub struct GameKey { #[extends] From 9e635ba8277fbbcd1fc9607ef66637c862d23350 Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 23 Jun 2026 15:41:04 +0200 Subject: [PATCH 26/29] i got the gamekey wrong --- rnex-core/src/nex/friends_handler.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index a7f39d3..8a896e7 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -197,7 +197,7 @@ impl Friends3DS for FriendsUser { let dummy = FriendRelationship { data: Data {}, pid: 69, - local_friend_code: 1, + local_friend_code: 3268487429723707977, relationship_type: 1, }; @@ -224,7 +224,7 @@ impl Friends3DS for FriendsUser { let dummy = FriendRelationship { data: Data {}, pid: 69, - local_friend_code: 1, + local_friend_code: 3268487429723707977, relationship_type: 1, }; @@ -241,7 +241,7 @@ impl Friends3DS for FriendsUser { let dummy = FriendRelationship { data: Data {}, pid: 69, - local_friend_code: 1, + local_friend_code: 3268487429723707977, relationship_type: 1, }; @@ -267,6 +267,8 @@ impl Friends3DS for FriendsUser { } async fn get_friend_presence(&self, unk: Vec) -> Result, ErrorCode> { + log::info!("pids: {:?}", unk); + let presence = FriendPresence { data: Data {}, pid: 69, @@ -275,8 +277,8 @@ impl Friends3DS for FriendsUser { changed_bit_flag: 0, game_key: rnex_core::rmc::protocols::friends_3ds::GameKey { data: Data {}, - title_id: 0x0005000010176900, - version: 0, + title_id: 1125899907457280, + version: 2064, }, game_mode_desctiption: "".to_string(), join_availibility_flag: 0, @@ -311,8 +313,8 @@ impl Friends3DS for FriendsUser { platform: 0, game_key: rnex_core::rmc::protocols::friends_3ds::GameKey { data: Data {}, - title_id: 0x0005000010176900, - version: 0, + title_id: 1125899907457280, + version: 2064, }, message: "yo whats up".to_string(), msg_updated_at: KerberosDateTime::now(), From 7bba2cddd45b29a4013df83e631530e384de541a Mon Sep 17 00:00:00 2001 From: red binder Date: Tue, 23 Jun 2026 16:01:21 +0200 Subject: [PATCH 27/29] bit flag --- rnex-core/src/nex/friends_handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index 8a896e7..6980744 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -274,7 +274,7 @@ impl Friends3DS for FriendsUser { pid: 69, presence: NintendoPresence { data: Data {}, - changed_bit_flag: 0, + changed_bit_flag: 0xFFFFFFFF, game_key: rnex_core::rmc::protocols::friends_3ds::GameKey { data: Data {}, title_id: 1125899907457280, From fcc2f942368b04e84789b7824e82cc6ad2021fb4 Mon Sep 17 00:00:00 2001 From: Maple Nebel Date: Sun, 5 Jul 2026 22:26:36 +0200 Subject: [PATCH 28/29] nex-account support for nnas --- Cargo.lock | 89 +++++++++-- rnex-core/src/nex/friends_handler.rs | 224 +++++++++++++-------------- 2 files changed, 184 insertions(+), 129 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9ad2257..9946174 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,17 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aes" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fc76eaeac4c9164506c466d4ffdd8ec9d0c5bf57ee97177c4d8eceb3a0e138" +dependencies = [ + "cipher 0.5.1", + "cpubits", + "cpufeatures 0.3.0", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -719,7 +730,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common 0.1.7", - "inout", + "inout 0.1.4", +] + +[[package]] +name = "cipher" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e34d8227fe1ba289043aeb13792056ff80fd6de1a9f49137a5f499de8e8c78ea" +dependencies = [ + "crypto-common 0.2.1", + "inout 0.2.2", ] [[package]] @@ -803,6 +824,12 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpubits" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -1084,7 +1111,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -1579,10 +1606,11 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hybrid-array" -version = "0.4.11" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d46837a0ed51fe95bd3b05de33cd64a1ee88fc797477ca48446872504507c5" +checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" dependencies = [ + "bytemuck", "typenum", ] @@ -1693,7 +1721,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.3", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -1853,6 +1881,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "inout" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" +dependencies = [ + "hybrid-array", +] + [[package]] name = "ipnet" version = "2.12.0" @@ -2091,16 +2128,20 @@ checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" [[package]] name = "nex-account" -version = "0.2.1" +version = "0.2.3" source = "sparse+https://crates.spbr.net/api/v1/crates/" -checksum = "51215ae67dfb36017956435ee4935bd62d64b62a8dd716c4614e11c8c29f941e" +checksum = "2dd9e8b23a09bb5ab8aeb5f39ed5ed1861549e4294757c9be30eb281c4ad11d0" dependencies = [ + "base64", + "bytemuck", "hmac 0.13.0", "log", "md-5 0.11.0", "prost", "simplelog", + "spbr-common", "sqlx 0.9.0", + "thiserror", "tokio", "tonic", "tonic-prost", @@ -2568,9 +2609,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" dependencies = [ "chacha20", "getrandom 0.4.2", @@ -2627,7 +2668,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f1256e23efe6097f27aa82d6ca6889361c001586ae0f6917cbad072f05eb275" dependencies = [ - "cipher", + "cipher 0.4.4", ] [[package]] @@ -2732,7 +2773,7 @@ dependencies = [ "nex-account", "once_cell", "paste", - "rand 0.10.1", + "rand 0.10.2", "rc4", "serde", "serde_json", @@ -2787,7 +2828,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3128,6 +3169,22 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "spbr-common" +version = "0.1.0" +source = "sparse+https://crates.spbr.net/api/v1/crates/" +checksum = "04f4e4710d23bb730a95d00b853e304a4c1c9e86276792cd32a92057e9833452" +dependencies = [ + "aes", + "base64", + "bytemuck", + "hmac 0.13.0", + "hybrid-array", + "md-5 0.11.0", + "rand 0.10.2", + "thiserror", +] + [[package]] name = "spin" version = "0.9.8" @@ -3465,7 +3522,7 @@ dependencies = [ "log", "md-5 0.11.0", "memchr", - "rand 0.10.1", + "rand 0.10.2", "serde", "serde_json", "sha2 0.11.0", @@ -3595,10 +3652,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4237,7 +4294,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index 6980744..7686462 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -4,8 +4,9 @@ use std::ops::Deref; use std::sync::{Arc, atomic::AtomicU32}; use std::sync::{LazyLock, Weak}; -use base64::{engine::general_purpose, Engine as _}; -use bytemuck::{bytes_of, Pod, Zeroable}; +use base64::{Engine as _, engine::general_purpose}; +use bytemuck::{Pod, Zeroable, bytes_of}; +use hex::decode; use hmac::Mac; use log::info; use macros::rmc_struct; @@ -13,12 +14,12 @@ use rnex_core::rmc::protocols::account_management::{ AccountExtraInfo, AccountManagement, RawAccountManagement, RawAccountManagementInfo, RemoteAccountManagement, }; -use rnex_core::rmc::protocols::friends_wiiu::{ - FriendsWiiU, RawFriendsWiiU, RawFriendsWiiUInfo, RemoteFriendsWiiU, -}; use rnex_core::rmc::protocols::friends_3ds::{ Friends3DS, RawFriends3DS, RawFriends3DSInfo, RemoteFriends3DS, }; +use rnex_core::rmc::protocols::friends_wiiu::{ + FriendsWiiU, RawFriendsWiiU, RawFriendsWiiUInfo, RemoteFriendsWiiU, +}; use rnex_core::rmc::protocols::nintendo_notification::{ NintendoNotification, RawNintendoNotification, RawNintendoNotificationInfo, RemoteNintendoNotification, @@ -40,7 +41,6 @@ use rnex_core::{ }; use sqlx::query; use std::sync::atomic::Ordering::Relaxed; -use hex::decode; use tokio::spawn; use tokio::sync::RwLock; @@ -56,10 +56,13 @@ use rnex_core::rmc::structures::data::Data; use crate::executables::common::get_db; -use nex_account::derive_pid_hmac; +use crate::rmc::protocols::friends_3ds::{ + FriendComment, FriendMii, FriendMiiList, FriendPersistentInfo, FriendPicture, FriendPresence, + FriendRelationship, Mii, MiiList, MyProfile, NintendoPresence, PlayedGame, +}; use nex_account::grpc::ActCreateInfo; use nex_account::grpc::nex_account_service_client::NexAccountServiceClient; -use crate::rmc::protocols::friends_3ds::{FriendComment, FriendMii, FriendMiiList, FriendPersistentInfo, FriendPicture, FriendPresence, FriendRelationship, Mii, MiiList, MyProfile, NintendoPresence, PlayedGame}; +use nex_account::{derive_pid_hmac, grpc_client}; define_rmc_proto!( proto FriendsUser{ @@ -96,7 +99,6 @@ pub struct NascToken { pub pwd_hash: [u8; 4], } - #[rmc_struct(FriendsUser)] pub struct FriendsUser { pub fm: Arc, @@ -143,26 +145,29 @@ impl Friends3DS for FriendsUser { Ok(()) } - async fn update_preference(&self, show_online_status: bool, show_current_title: bool, block_friend_requests: bool) -> Result<(), ErrorCode> { + async fn update_preference( + &self, + show_online_status: bool, + show_current_title: bool, + block_friend_requests: bool, + ) -> Result<(), ErrorCode> { // stubbed Ok(()) } - async fn get_friend_mii(&self, friends: Vec) -> Result, ErrorCode> { + async fn get_friend_mii( + &self, + friends: Vec, + ) -> Result, ErrorCode> { // sorry for the copying pretendo but i don't have a mii on hand rn let data: Vec = vec![ - 0x03, 0x00, 0x00, 0x40, 0xE9, 0x55, 0xA2, 0x09, - 0xE7, 0xC7, 0x41, 0x82, 0xD9, 0x7D, 0x0B, 0x2D, - 0x03, 0xB3, 0xB8, 0x8D, 0x27, 0xD9, 0x00, 0x00, - 0x01, 0x40, 0x62, 0x00, 0x65, 0x00, 0x6C, 0x00, - 0x6C, 0x00, 0x61, 0x00, 0x00, 0x00, 0x45, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, - 0x12, 0x00, 0x81, 0x01, 0x04, 0x68, 0x43, 0x18, - 0x20, 0x34, 0x46, 0x14, 0x81, 0x12, 0x17, 0x68, - 0x0D, 0x00, 0x00, 0x29, 0x03, 0x52, 0x48, 0x50, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x86, + 0x03, 0x00, 0x00, 0x40, 0xE9, 0x55, 0xA2, 0x09, 0xE7, 0xC7, 0x41, 0x82, 0xD9, 0x7D, + 0x0B, 0x2D, 0x03, 0xB3, 0xB8, 0x8D, 0x27, 0xD9, 0x00, 0x00, 0x01, 0x40, 0x62, 0x00, + 0x65, 0x00, 0x6C, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x12, 0x00, 0x81, 0x01, 0x04, 0x68, 0x43, 0x18, + 0x20, 0x34, 0x46, 0x14, 0x81, 0x12, 0x17, 0x68, 0x0D, 0x00, 0x00, 0x29, 0x03, 0x52, + 0x48, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x86, ]; let dummymii = FriendMii { @@ -181,19 +186,33 @@ impl Friends3DS for FriendsUser { Ok(vec![dummymii]) } - async fn get_friend_mii_list(&self, friends: Vec) -> Result, ErrorCode> { + async fn get_friend_mii_list( + &self, + friends: Vec, + ) -> Result, ErrorCode> { Err(ErrorCode::Core_NotImplemented) } - async fn is_active_game(&self, unk: Vec, game_key: crate::rmc::protocols::friends_3ds::GameKey) -> Result, ErrorCode> { + async fn is_active_game( + &self, + unk: Vec, + game_key: crate::rmc::protocols::friends_3ds::GameKey, + ) -> Result, ErrorCode> { Err(ErrorCode::Core_NotImplemented) } - async fn get_principal_id_by_local_friend_code(&self, unk1: u64, unk2: Vec) -> Result, ErrorCode> { + async fn get_principal_id_by_local_friend_code( + &self, + unk1: u64, + unk2: Vec, + ) -> Result, ErrorCode> { Err(ErrorCode::Core_NotImplemented) } - async fn get_friend_relationships(&self, unk2: Vec) -> Result, ErrorCode> { + async fn get_friend_relationships( + &self, + unk2: Vec, + ) -> Result, ErrorCode> { let dummy = FriendRelationship { data: Data {}, pid: 69, @@ -208,7 +227,11 @@ impl Friends3DS for FriendsUser { Err(ErrorCode::Core_NotImplemented) } - async fn add_friend_by_lst_pid(&self, unk: u64, pid: Vec) -> Result, ErrorCode> { + async fn add_friend_by_lst_pid( + &self, + unk: u64, + pid: Vec, + ) -> Result, ErrorCode> { Err(ErrorCode::Core_NotImplemented) } @@ -235,7 +258,12 @@ impl Friends3DS for FriendsUser { Ok(()) } - async fn sync_friend(&self, unk1: u64, unk2: Vec, unk3: Vec) -> Result, ErrorCode> { + async fn sync_friend( + &self, + unk1: u64, + unk2: Vec, + unk3: Vec, + ) -> Result, ErrorCode> { log::info!("params: {:?}, {:?}, {:?}", unk1, unk2, unk3); let dummy = FriendRelationship { @@ -248,11 +276,18 @@ impl Friends3DS for FriendsUser { Ok(vec![dummy]) } - async fn update_presence(&self, nintendo_presence: NintendoPresence, unk: bool) -> Result<(), ErrorCode> { + async fn update_presence( + &self, + nintendo_presence: NintendoPresence, + unk: bool, + ) -> Result<(), ErrorCode> { Ok(()) } - async fn update_favorite_game_key(&self, game_key: rnex_core::rmc::protocols::friends_3ds::GameKey) -> Result<(), ErrorCode> { + async fn update_favorite_game_key( + &self, + game_key: rnex_core::rmc::protocols::friends_3ds::GameKey, + ) -> Result<(), ErrorCode> { log::info!("favorite game key: {:?}", game_key); Ok(()) @@ -294,7 +329,10 @@ impl Friends3DS for FriendsUser { Ok(vec![presence]) } - async fn get_friend_comment(&self, unk: Vec) -> Result, ErrorCode> { + async fn get_friend_comment( + &self, + unk: Vec, + ) -> Result, ErrorCode> { Err(ErrorCode::Core_NotImplemented) } @@ -302,7 +340,10 @@ impl Friends3DS for FriendsUser { Err(ErrorCode::Core_NotImplemented) } - async fn get_friend_persistent_info(&self, unk: Vec) -> Result, ErrorCode> { + async fn get_friend_persistent_info( + &self, + unk: Vec, + ) -> Result, ErrorCode> { let dummypersistentinfo = FriendPersistentInfo { data: Data {}, pid: 69, @@ -508,29 +549,6 @@ impl Secure for FriendsGuest { } } - -// this is probably a horrible way to do this. it's 1:27am. fuck off please i'll fix it later. -fn decode_token(encoded_str: &str) -> Result { - // i dont like the use of replace here as this will reallocate the entire string - // im gonna keep it though as long as noone comes up with a good alternative for this - // without complicating the code/making it far less readable/unnescesarily long - let standard_b64 = encoded_str.replace('.', "+").replace('-', "/").replace('*', "="); - - let standard = general_purpose::STANDARD.decode(standard_b64).map_err(|_| "failed to convert")?; - - let bytes = general_purpose::STANDARD - .decode(standard) - .map_err(|_| "failed to decode Base64 string")?; - - if bytes.len() != std::mem::size_of::() { - return Err("decoded byte length mismatch"); - } - - let token = bytemuck::from_bytes::(&bytes); - - Ok(*token) -} - impl AccountManagement for FriendsGuest { async fn nintendo_create_account( &self, @@ -542,70 +560,50 @@ impl AccountManagement for FriendsGuest { ) -> Result<(PID, String), ErrorCode> { println!("{}, {}, {}, {}", principal_name, key, groups, email); - if let Ok(data) = auth_data.try_get_as::() { - let pid = data.nna_info.principal_basic_info.pid; - info!("create account via standard data: {}", pid); + let nex_token = if let Ok(extra_info) = auth_data.try_get_as::() { + extra_info.nex_token + } else if let Ok(data) = auth_data.try_get_as::() { + data.nex_token + } else { + return Err(ErrorCode::Authentication_InvalidParam); + }; + let (pid, nex_key) = nex_account::decode_nexact_token(&nex_token).map_err(|e| { + log::error!("failed to decode token: {}", e); + log::info!("{:?}", nex_token); + ErrorCode::Authentication_InvalidParam + })?; - let nexkey: [u8; 16] = hex::decode(key) - .map_err(|_| ErrorCode::Authentication_InvalidParam)? - .as_slice() - .try_into() - .map_err(|_| ErrorCode::Authentication_InvalidParam)?; + //let mac = derive_pid_hmac(data.nna_info.principal_basic_info.pid, &nexkey); - let mac = derive_pid_hmac(data.nna_info.principal_basic_info.pid, &nexkey); + let mut client = grpc_client().await.map_err(|e| { + eprintln!("error occurred: {:?}", e); + ErrorCode::Core_Unknown + })?; - let hex_str = hex::encode(mac); - return Ok((pid, hex_str)); + let new_account: ActCreateInfo = ActCreateInfo { + principal_name, + key: nex_key.into(), + email, + pid, + }; + + let nexkey = client + .create_new_sequential_or_update_and_get_account(new_account) + .await + .map_err(|e| ErrorCode::Core_Unknown)? + .into_inner(); + + if nexkey.key.len() != 16 { + log::error!("nex key was not 16 bytes long"); + return Err(ErrorCode::Authentication_InvalidParam); } - if let Ok(extra_info) = auth_data.try_get_as::() { - info!("create account via extra info"); + let nexkeyarray: [u8; 16] = nexkey.key.try_into().expect("how...?"); - let decoded_token = decode_token(&*extra_info.nex_token).map_err(|e| { - log::error!("failed to decode token: {}", e); - log::info!("{:?}", extra_info.nex_token); - ErrorCode::Authentication_InvalidParam - })?; + let mac = derive_pid_hmac(pid, &nexkeyarray); - log::info!("decoded token: {:?}", decoded_token); + let hex_str = hex::encode(mac); - let mut client = NexAccountServiceClient::connect(NEX_ACCOUNT_URL.as_str()) - .await - .map_err(|e| { - eprintln!("error occurred: {:?}", e); - ErrorCode::Core_Unknown - })?; - - let pid = decoded_token.pid; - - let new_account: ActCreateInfo = ActCreateInfo { - principal_name, - key: vec![], - email, - pid, - }; - - let nexkey = client - .create_new_sequential_or_update_and_get_account(new_account) - .await - .map_err(|e| {ErrorCode::Core_Unknown})? - .into_inner(); - - if nexkey.key.len() != 16 { - log::error!("nex key was not 16 bytes long"); - return Err(ErrorCode::Authentication_InvalidParam); - } - - let nexkeyarray: [u8; 16] = nexkey.key.try_into() - .expect("how...?"); - - let mac = derive_pid_hmac(pid, &nexkeyarray); - - let hex_str = hex::encode(mac); - - return Ok((pid, hex_str)); - } - - Err(ErrorCode::Authentication_InvalidParam) + return Ok((pid, hex_str)); } } From 9a11863aa74a3b82c2f8db04f570b0a33d246605 Mon Sep 17 00:00:00 2001 From: Maple Nebel Date: Mon, 6 Jul 2026 13:26:05 +0200 Subject: [PATCH 29/29] stub friends function --- rnex-core/src/nex/friends_handler.rs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/rnex-core/src/nex/friends_handler.rs b/rnex-core/src/nex/friends_handler.rs index 7686462..31d4022 100644 --- a/rnex-core/src/nex/friends_handler.rs +++ b/rnex-core/src/nex/friends_handler.rs @@ -392,7 +392,27 @@ impl FriendsWiiU for FriendsUser { ErrorCode, > { // let query = query!("select ", self.pid).fetch_all(get_db()).await; - Err(ErrorCode::Core_NotImplemented) + Ok(( + PrincipalPreference { + data: Data {}, + block_friend_request: false, + show_online: true, + show_playing_title: false, + }, + Comment { + data: Data {}, + last_changed: KerberosDateTime::now(), + message: "stub(will be impl'd later)".into(), + unk: 0, + }, + vec![], + vec![], + vec![], + vec![], + false, + vec![], + false, + )) } async fn add_friend(&self, friend: PID) -> Result<(FriendRequest, FriendInfo), ErrorCode> {