diff --git a/Cargo.lock b/Cargo.lock index e636f74..09a45cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,27 +10,21 @@ dependencies = [ "argon2", "base64", "bcrypt", - "binrw", "bytemuck", "cbc", "chrono", "crc32fast", "dotenvy", - "dsa", - "ecdsa", "gxhash", "hex", "hmac", "juniper", "juniper_rocket", - "k256", "lettre", "log", "md-5", "mii", "once_cell", - "openssl", - "p256", "prost", "quick-xml", "rand 0.8.5", @@ -41,7 +35,6 @@ dependencies = [ "serde", "serde_json", "sha2", - "sha256", "sqlx", "thiserror", "time", @@ -288,12 +281,6 @@ dependencies = [ "password-hash", ] -[[package]] -name = "array-init" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc" - [[package]] name = "async-stream" version = "0.3.6" @@ -437,12 +424,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - [[package]] name = "base64" version = "0.22.1" @@ -474,30 +455,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" -[[package]] -name = "binrw" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53195f985e88ab94d1cc87e80049dd2929fd39e4a772c5ae96a7e5c4aad3642" -dependencies = [ - "array-init", - "binrw_derive", - "bytemuck", -] - -[[package]] -name = "binrw_derive" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5910da05ee556b789032c8ff5a61fb99239580aa3fd0bfaa8f4d094b2aee00ad" -dependencies = [ - "either", - "owo-colors", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "bitflags" version = "2.8.0" @@ -740,18 +697,6 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - [[package]] name = "crypto-common" version = "0.1.6" @@ -886,36 +831,6 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" -[[package]] -name = "dsa" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48bc224a9084ad760195584ce5abb3c2c34a225fa312a128ad245a6b412b7689" -dependencies = [ - "digest", - "num-bigint-dig", - "num-traits", - "pkcs8", - "rfc6979", - "sha2", - "signature", - "zeroize", -] - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - [[package]] name = "either" version = "1.13.0" @@ -925,26 +840,6 @@ dependencies = [ "serde", ] -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array", - "group", - "pem-rfc7468", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle", - "zeroize", -] - [[package]] name = "email-encoding" version = "0.4.1" @@ -1014,16 +909,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "ff" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "figment" version = "0.10.19" @@ -1224,7 +1109,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", - "zeroize", ] [[package]] @@ -1262,17 +1146,6 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "gxhash" version = "3.5.0" @@ -1898,20 +1771,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "k256" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "once_cell", - "sha2", - "signature", -] - [[package]] name = "language-tags" version = "0.3.2" @@ -2243,9 +2102,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "openssl" -version = "0.10.78" +version = "0.10.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38c4372413cdaaf3cc79dd92d29d7d9f5ab09b51b10dded508fb90bb70b9222" +checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" dependencies = [ "bitflags", "cfg-if", @@ -2275,9 +2134,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.114" +version = "0.9.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13ce1245cd07fcc4cfdb438f7507b0c7e4f3849a69fd84d52374c66d83741bb6" +checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" dependencies = [ "cc", "libc", @@ -2302,24 +2161,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" -[[package]] -name = "owo-colors" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - [[package]] name = "parking" version = "2.2.1" @@ -2492,15 +2333,6 @@ dependencies = [ "syn", ] -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - [[package]] name = "proc-macro2" version = "1.0.93" @@ -2792,16 +2624,6 @@ dependencies = [ "windows-registry", ] -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - [[package]] name = "ring" version = "0.17.11" @@ -3035,20 +2857,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - [[package]] name = "security-framework" version = "2.11.1" @@ -3282,19 +3090,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sha256" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f880fc8562bdeb709793f00eb42a2ad0e672c4f883bbe59122b926eca935c8f6" -dependencies = [ - "async-trait", - "bytes", - "hex", - "sha2", - "tokio", -] - [[package]] name = "sharded-slab" version = "0.1.7" diff --git a/Cargo.toml b/Cargo.toml index 8d5123e..dfc5b32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,13 +48,6 @@ prost = "0.13.4" lettre = "0.11.15" rand = "0.8.5" reqwest = "0.12.12" -binrw = "0.15.1" -ecdsa = { version = "0.16.9", features = ["pem", "std", "verifying"] } -sha256 = "1.6.0" -p256 = "0.13.2" -k256 = "0.13.4" -dsa = "0.6.3" -openssl = "0.10.78" time = "0.3.47" diff --git a/grpc-protobufs b/grpc-protobufs index 4101111..405fe9b 160000 --- a/grpc-protobufs +++ b/grpc-protobufs @@ -1 +1 @@ -Subproject commit 410111190ec9f540d60108b70d55a437a6caf68e +Subproject commit 405fe9b47b416e76b21d7087b2ed11606deccfcf diff --git a/src/account/account.rs b/src/account/account.rs index df198bc..1640cad 100644 --- a/src/account/account.rs +++ b/src/account/account.rs @@ -1,39 +1,38 @@ -use std::io::{Cursor, Write}; +use std::io::Write; use std::ops::{Deref, DerefMut}; // Don't import until required. // use argon2::{Algorithm, Argon2, PasswordHash, PasswordHasher, PasswordVerifier}; // use argon2::password_hash::rand_core::OsRng; // use argon2::password_hash::SaltString; -use crate::Pool; -use crate::error::{Error, Errors}; -use crate::nnid::oauth::TokenData; use base64::Engine; use base64::prelude::BASE64_STANDARD; -use binrw::{BinRead, binread}; use bytemuck::bytes_of; use chrono::{NaiveDate, NaiveDateTime, Utc}; -use openssl::bn::BigNum; -use openssl::ecdsa::EcdsaSig; -use rand::Rng; use rocket::http::Status; +use rocket::{async_trait, Request}; use rocket::request::{FromRequest, Outcome}; -use rocket::{Request, async_trait}; use sha2::{Digest, Sha256}; +use crate::error::{Error, Errors}; +use crate::nnid::oauth::TokenData; +use crate::Pool; +use rand::Rng; macro_rules! request_try { ($expression:expr) => { - match $expression { + match $expression{ Ok(v) => v, - Err(e) => return Outcome::Error((Status::BadRequest, e)), + Err(e) => return Outcome::Error((Status::BadRequest, e)) } }; } -const INVALID_TOKEN_ERRORS: Errors<'static> = Errors { - error: &[Error { - message: "Invalid access token", - code: "0005", - }], +const INVALID_TOKEN_ERRORS: Errors<'static> = Errors{ + error: &[ + Error{ + message: "Invalid access token", + code: "0005" + } + ] }; // optimization note: add token caching @@ -57,48 +56,52 @@ pub struct User { pub creation_date: NaiveDateTime, pub updated: NaiveDateTime, pub nex_password: String, - pub verification_code: Option, + pub verification_code: Option } -fn generate_nintendo_hash(pid: i32, text_password: &str) -> String { +fn generate_nintendo_hash(pid: i32, text_password: &str) -> String{ let mut sha = Sha256::new(); sha.write_all(&bytes_of(&pid)).unwrap(); - sha.write_all(&[0x02, 0x65, 0x43, 0x46]).unwrap(); + sha.write_all(&[0x02, 0x65, 0x43 ,0x46]).unwrap(); sha.write_all(text_password.as_bytes()).unwrap(); hex::encode(&sha.finalize()[..]) } -impl User { - pub async fn get_by_username(name: &str, pool: &Pool) -> Option { - sqlx::query_as!(Self, "SELECT * FROM users WHERE username = $1", name) - .fetch_one(pool) +impl User{ + pub async fn get_by_username(name: &str, pool: &Pool) -> Option{ + sqlx::query_as!( + Self, + "SELECT * FROM users WHERE username = $1", + name + ).fetch_one(pool) .await .ok() } - fn generate_nintendo_hash(&self, text_password: &str) -> String { + fn generate_nintendo_hash(&self, text_password: &str) -> String{ generate_nintendo_hash(self.pid, text_password) } - pub fn verify_cleartext_password(&self, cleartext_password: &str) -> Option { + pub fn verify_cleartext_password(&self, cleartext_password: &str) -> Option{ let nintendo_hash = self.generate_nintendo_hash(cleartext_password); self.verify_hashed_password(&nintendo_hash) } - pub fn verify_hashed_password(&self, hashed_password: &str) -> Option { + pub fn verify_hashed_password(&self, hashed_password: &str) -> Option{ bcrypt::verify(hashed_password, &self.password).ok() } } -pub fn generate_password(pid: i32, cleartext_password: &str) -> Option { +pub fn generate_password(pid: i32, cleartext_password: &str) -> Option{ let password = generate_nintendo_hash(pid, cleartext_password); bcrypt::hash(password, 10).ok() } + pub async fn read_basic_auth_token(connection: &Pool, token: &str) -> Option { let data = match BASE64_STANDARD.decode(&token) { Ok(d) => d, @@ -128,9 +131,7 @@ pub async fn read_basic_auth_token(connection: &Pool, token: &str) -> Option u, @@ -149,27 +150,26 @@ pub async fn read_basic_auth_token(connection: &Pool, token: &str) -> Option Option { let data = TokenData::decode(token)?; - let token_info = sqlx::query!( - "select * from tokens where pid = $1 and token_id = $2 and random =$3", - data.pid, - data.token_id, - data.random - ) - .fetch_one(connection) - .await - .ok()?; + let token_info = + sqlx::query!( + "select * from tokens where pid = $1 and token_id = $2 and random =$3", + data.pid, data.token_id, data.random + ). + fetch_one(connection).await.ok()?; - if token_info.expires.and_utc() < Utc::now() { - return None; + if token_info.expires.and_utc() < Utc::now(){ + return None } - let user = sqlx::query_as!(User, "SELECT * FROM users WHERE pid = $1", token_info.pid) - .fetch_one(connection) - .await - .ok()?; + let user = sqlx::query_as!( + User, + "SELECT * FROM users WHERE pid = $1", + token_info.pid + ).fetch_one(connection).await.ok()?; Some(user) } @@ -180,7 +180,7 @@ pub fn generate_nex_password() -> String { while output.len() < 16 { let offset: u8 = rng.gen_range(0..62); - + let character = if offset < 10 { (offset + b'0') as char } else if offset < 36 { @@ -188,77 +188,59 @@ pub fn generate_nex_password() -> String { } else { (offset + 61) as char }; - + output.push(character); } output } -pub struct Auth( - pub User, -); +pub struct Auth(pub User); -impl AsRef - for Auth -{ +impl AsRef for Auth{ fn as_ref(&self) -> &User { &self.0 } } -impl AsMut - for Auth -{ +impl AsMut for Auth{ fn as_mut(&mut self) -> &mut User { &mut self.0 } } -impl Deref - for Auth -{ +impl Deref for Auth{ type Target = User; fn deref(&self) -> &Self::Target { &self.0 } } -impl DerefMut - for Auth -{ +impl DerefMut for Auth{ fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } -impl Into - for Auth -{ +impl Into for Auth{ fn into(self) -> User { self.0 } } + + #[async_trait] -impl<'r, const FORCE_BEARER_AUTH: bool, const USE_CERT: bool> FromRequest<'r> - for Auth -{ +impl<'r, const FORCE_BEARER_AUTH: bool> FromRequest<'r> for Auth{ type Error = Errors<'static>; async fn from_request(request: &'r Request<'_>) -> Outcome { let pool: &Pool = request.rocket().state().unwrap(); - let auth = request_try!( - request - .headers() - .get("Authorization") - .next() - .ok_or(INVALID_TOKEN_ERRORS) - ); + let auth = request_try!(request.headers().get("Authorization").next().ok_or(INVALID_TOKEN_ERRORS)); let (auth_type, token) = request_try!(auth.split_once(' ').ok_or(INVALID_TOKEN_ERRORS)); - let user = match auth_type { + let user = match auth_type{ "Basic" if !FORCE_BEARER_AUTH => read_basic_auth_token(pool, token).await, "Bearer" => read_bearer_auth_token(pool, token).await, _ => return Outcome::Error((Status::BadRequest, INVALID_TOKEN_ERRORS)), @@ -268,24 +250,10 @@ impl<'r, const FORCE_BEARER_AUTH: bool, const USE_CERT: bool> FromRequest<'r> return Outcome::Error((Status::BadRequest, INVALID_TOKEN_ERRORS)); }; - if user.account_level < 0 { + if user.account_level < 0{ return Outcome::Error((Status::BadRequest, INVALID_TOKEN_ERRORS)); } - - if USE_CERT { - let cert = request_try!( - request - .headers() - .get("X-Nintendo-Device-Cert") - .next() - .ok_or(INVALID_TOKEN_ERRORS) - ); - - let Some(cert) = Certificate::new(&cert) else { - return Outcome::Error((Status::BadGateway, INVALID_TOKEN_ERRORS)); - }; - } - + // let user = User{ // nex_password: format!("{:a>16}", user.nex_password), // ..user @@ -293,80 +261,4 @@ impl<'r, const FORCE_BEARER_AUTH: bool, const USE_CERT: bool> FromRequest<'r> Outcome::Success(Self(user)) } -} - -#[binread] -#[br(big)] -#[derive(Debug)] -pub struct Certificate { - issuer: [u8; 0x40], - key_type: u32, - cert_name: [u8; 0x40], - key_id: u32, - pubkey_data: [u8; 0x40], -} - -#[binread] -#[br(big)] -#[br(magic(0x10005u32))] -struct OuterCertificate { - signature: [u8; 0x3C], - padding: [u8; 0x40], - data: [u8; 0x100], -} - -const PUB_PEM: &[u8] = br#"-----BEGIN PUBLIC KEY----- -MFIwEAYHKoZIzj0CAQYFK4EEABsDPgAEAP1WBBgs8XUJIQDDCK5IOZEbb5+h1TqV -rwgzSUcrAAFxMWm1kf/TDL9z2nZkuo0N+VtNEQREZDXA7aQv ------END PUBLIC KEY-----"#; - -impl Certificate { - fn new(data: &str) -> Option { - let data = BASE64_STANDARD.decode(data).unwrap(); - - let cert = OuterCertificate::read(&mut Cursor::new(&data)).ok()?; - - println!("key"); - - let key = openssl::ec::EcKey::public_key_from_pem(PUB_PEM).ok()?; - - let sig_components = read_p1363(&cert.signature)?; - - let sig = EcdsaSig::from_private_components(sig_components.0, sig_components.1).unwrap(); - - let mut hasher = openssl::sha::Sha256::new(); - - hasher.update(&cert.data); - - let hash = hasher.finish(); - - if !sig.verify(&hash[..], &key).ok()? { - return None; - } - - Certificate::read(&mut Cursor::new(cert.data)).ok() - } - - pub fn hash(&self) -> [u8; 32] { - let mut hasher = openssl::sha::Sha256::new(); - - hasher.update(&self.issuer[..]); - hasher.update(bytes_of(&self.key_id)); - hasher.update(&self.cert_name[..]); - hasher.update(bytes_of(&self.key_type)); - hasher.update(&self.pubkey_data[..]); - - hasher.finish() - } -} - -fn read_p1363(data: &[u8]) -> Option<(BigNum, BigNum)> { - if data.len() % 2 != 0 { - return None; - } - let half_len = data.len() / 2; - Some(( - BigNum::from_slice(&data[..half_len]).ok()?, - BigNum::from_slice(&data[half_len..]).ok()?, - )) -} +} \ No newline at end of file