mirror of
https://git.ethanthesleepy.one/ethanaobrien/ew
synced 2026-08-26 23:12:20 +08:00
Cleanly handle unverified gree devices
This commit is contained in:
@@ -60,9 +60,20 @@ fn vacuum_database() {
|
|||||||
DATABASE.lock_and_exec("VACUUM", params!());
|
DATABASE.lock_and_exec("VACUUM", params!());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_registered(uuid: &str) -> bool {
|
||||||
|
match DATABASE.lock_and_select("SELECT cert FROM users WHERE uuid=?1;", params!(uuid)) {
|
||||||
|
Ok(cert) => cert != "none",
|
||||||
|
Err(_) => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn verify_signature(signature: &[u8], message: &[u8], public_key: &str) -> bool {
|
fn verify_signature(signature: &[u8], message: &[u8], public_key: &str) -> bool {
|
||||||
let pem = pem::parse(public_key).unwrap();
|
let Ok(pem) = pem::parse(public_key) else {
|
||||||
let public_key = RsaPublicKey::from_public_key_der(&pem.contents()).unwrap();
|
return false;
|
||||||
|
};
|
||||||
|
let Ok(public_key) = RsaPublicKey::from_public_key_der(&pem.contents()) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
let digest = Sha1::digest(message);
|
let digest = Sha1::digest(message);
|
||||||
|
|
||||||
public_key
|
public_key
|
||||||
@@ -80,7 +91,9 @@ pub fn get_uuid(headers: &HeaderMap, body: &str) -> String {
|
|||||||
return String::new();
|
return String::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
let cert = DATABASE.lock_and_select("SELECT cert FROM users WHERE user_id=?1;", params!(uid)).unwrap();
|
let Ok(cert) = DATABASE.lock_and_select("SELECT cert FROM users WHERE user_id=?1;", params!(uid)) else {
|
||||||
|
return String::new();
|
||||||
|
};
|
||||||
|
|
||||||
let data = format!("{}{}{}{}{}", uid, "sk1bdzb310n0s9tl", version, timestamp, body);
|
let data = format!("{}{}{}{}{}", uid, "sk1bdzb310n0s9tl", version, timestamp, body);
|
||||||
let encoded = general_purpose::STANDARD.encode(data.as_bytes());
|
let encoded = general_purpose::STANDARD.encode(data.as_bytes());
|
||||||
|
|||||||
@@ -50,6 +50,27 @@ pub struct Body(pub JsonValue);
|
|||||||
|
|
||||||
pub struct Login(pub String);
|
pub struct Login(pub String);
|
||||||
|
|
||||||
|
struct SessionError(HttpRequest);
|
||||||
|
|
||||||
|
impl std::fmt::Debug for SessionError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "invalid session for uid {}", global::get_uid(self.0.headers()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for SessionError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl actix_web::ResponseError for SessionError {
|
||||||
|
fn error_response(&self) -> HttpResponse {
|
||||||
|
println!("Rejecting request from uid {}: bad session", global::get_uid(self.0.headers()));
|
||||||
|
global::api_error(&self.0, global::RESULT_SESSION)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Session {
|
pub struct Session {
|
||||||
pub key: String,
|
pub key: String,
|
||||||
pub body: JsonValue,
|
pub body: JsonValue,
|
||||||
@@ -93,9 +114,14 @@ impl FromRequest for Login {
|
|||||||
|
|
||||||
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
|
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
|
||||||
let headers = req.headers().clone();
|
let headers = req.headers().clone();
|
||||||
let fut = String::from_request(req, payload);
|
let req = req.clone();
|
||||||
|
let fut = String::from_request(&req, payload);
|
||||||
async move {
|
async move {
|
||||||
Ok(Login(global::get_login(&headers, &encryption::decrypt_packet(&fut.await?).unwrap())))
|
let key = global::get_login(&headers, &encryption::decrypt_packet(&fut.await?).unwrap());
|
||||||
|
if key.is_empty() {
|
||||||
|
return Err(SessionError(req).into());
|
||||||
|
}
|
||||||
|
Ok(Login(key))
|
||||||
}.boxed_local()
|
}.boxed_local()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,10 +132,14 @@ impl FromRequest for Session {
|
|||||||
|
|
||||||
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
|
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
|
||||||
let headers = req.headers().clone();
|
let headers = req.headers().clone();
|
||||||
let fut = String::from_request(req, payload);
|
let req = req.clone();
|
||||||
|
let fut = String::from_request(&req, payload);
|
||||||
async move {
|
async move {
|
||||||
let body = encryption::decrypt_packet(&fut.await?).unwrap();
|
let body = encryption::decrypt_packet(&fut.await?).unwrap();
|
||||||
let key = global::get_login(&headers, &body);
|
let key = global::get_login(&headers, &body);
|
||||||
|
if key.is_empty() {
|
||||||
|
return Err(SessionError(req).into());
|
||||||
|
}
|
||||||
Ok(Session { key, body: jzon::parse(&body).unwrap() })
|
Ok(Session { key, body: jzon::parse(&body).unwrap() })
|
||||||
}.boxed_local()
|
}.boxed_local()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,6 +118,8 @@ pub const RESULT_GAME_VERSION_UPDATED: i32 = 12;
|
|||||||
|
|
||||||
pub const RESULT_RESOURCE_UPDATED: i32 = 13;
|
pub const RESULT_RESOURCE_UPDATED: i32 = 13;
|
||||||
|
|
||||||
|
pub const RESULT_SESSION: i32 = 14;
|
||||||
|
|
||||||
pub const PROTOCOL_HEADER: &str = "X-Protocol-Version";
|
pub const PROTOCOL_HEADER: &str = "X-Protocol-Version";
|
||||||
|
|
||||||
pub fn client_protocol_version(req: &HttpRequest) -> u32 {
|
pub fn client_protocol_version(req: &HttpRequest) -> u32 {
|
||||||
@@ -230,11 +232,7 @@ pub fn get_login(headers: &HeaderMap, body: &str) -> String {
|
|||||||
Some(token) => {
|
Some(token) => {
|
||||||
token
|
token
|
||||||
},
|
},
|
||||||
None => {
|
None => gree::get_uuid(headers, body),
|
||||||
let rv = gree::get_uuid(headers, body);
|
|
||||||
assert!(rv != String::new());
|
|
||||||
rv
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use crate::database::gree::*;
|
|||||||
const APP_ID: &str = "232610769078541";
|
const APP_ID: &str = "232610769078541";
|
||||||
const SRC_APP_ID: &str = "100900301";
|
const SRC_APP_ID: &str = "100900301";
|
||||||
const HMAC_SECRET_HEX: &str = "6438663638653238346566646636306262616563326432323563306366643432";
|
const HMAC_SECRET_HEX: &str = "6438663638653238346566646636306262616563326432323563306366643432";
|
||||||
|
const ERROR_MIGRATED_DEVICE: i32 = 20;
|
||||||
|
|
||||||
struct RequireGreeAuth;
|
struct RequireGreeAuth;
|
||||||
|
|
||||||
@@ -84,7 +85,7 @@ fn get_uid(req: &HttpRequest) -> String {
|
|||||||
req.headers()
|
req.headers()
|
||||||
.get("Authorization")
|
.get("Authorization")
|
||||||
.and_then(|h| h.to_str().ok())
|
.and_then(|h| h.to_str().ok())
|
||||||
.and_then(|auth| auth.split(",xoauth_requestor_id=\"").nth(1))
|
.and_then(|auth| auth.split("xoauth_requestor_id=\"").nth(1))
|
||||||
.and_then(|s| s.split('"').next())
|
.and_then(|s| s.split('"').next())
|
||||||
.unwrap_or("")
|
.unwrap_or("")
|
||||||
.to_string()
|
.to_string()
|
||||||
@@ -95,13 +96,13 @@ fn send(req: HttpRequest, resp: JsonValue) -> impl Responder {
|
|||||||
jzon::stringify(resp)
|
jzon::stringify(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn not_found() -> impl Responder {
|
async fn not_found(req: HttpRequest) -> impl Responder {
|
||||||
let resp = object!{
|
let resp = object!{
|
||||||
code: 10001,
|
code: 10001,
|
||||||
message: "Not Found",
|
message: "Not Found",
|
||||||
result: "NG"
|
result: "NG"
|
||||||
};
|
};
|
||||||
jzon::stringify(resp)
|
send(req, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn initialize(req: HttpRequest, body: String) -> impl Responder {
|
async fn initialize(req: HttpRequest, body: String) -> impl Responder {
|
||||||
@@ -119,8 +120,17 @@ async fn initialize(req: HttpRequest, body: String) -> impl Responder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn authorize(req: HttpRequest, _body: String) -> impl Responder {
|
async fn authorize(req: HttpRequest, _body: String) -> impl Responder {
|
||||||
let resp = object!{
|
let resp = if is_registered(&get_uid(&req)) {
|
||||||
|
object!{
|
||||||
result: "OK"
|
result: "OK"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("Unregistered device authorizing: {}", get_uid(&req));
|
||||||
|
object!{
|
||||||
|
result: "NG",
|
||||||
|
code: ERROR_MIGRATED_DEVICE,
|
||||||
|
message: "Device is not registered"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
send(req, resp)
|
send(req, resp)
|
||||||
|
|||||||
@@ -65,15 +65,23 @@ pub enum UserView {
|
|||||||
Ranking,
|
Ranking,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEFAULT_CARD: i64 = 10010001;
|
||||||
|
|
||||||
fn proxy_card_id(id: i64) -> i64 {
|
fn proxy_card_id(id: i64) -> i64 {
|
||||||
let prefix = id / 10000;
|
let prefix = id / 10000;
|
||||||
if prefix < 10000 {
|
if prefix < 10000 {
|
||||||
id
|
return id;
|
||||||
} else if prefix < 14000 {
|
}
|
||||||
|
let rv = if prefix < 14000 {
|
||||||
(prefix - 9000) * 10000 + 1
|
(prefix - 9000) * 10000 + 1
|
||||||
} else {
|
} else {
|
||||||
10010001
|
DEFAULT_CARD
|
||||||
|
};
|
||||||
|
// Not every prefix has a real character behind it
|
||||||
|
if crate::router::databases::CARD_LIST[rv.to_string()].is_empty() {
|
||||||
|
return DEFAULT_CARD;
|
||||||
}
|
}
|
||||||
|
rv
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn proxy_user_cards(user: &mut JsonValue) {
|
pub fn proxy_user_cards(user: &mut JsonValue) {
|
||||||
|
|||||||
Reference in New Issue
Block a user