diff --git a/Cargo.lock b/Cargo.lock index 681eab6..8d99e2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -337,6 +337,12 @@ dependencies = [ "log", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "errno" version = "0.3.14" @@ -403,7 +409,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -461,6 +467,12 @@ dependencies = [ "wasip2", ] +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + [[package]] name = "hermit-abi" version = "0.5.2" @@ -490,6 +502,16 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "indexmap" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "io-uring" version = "0.7.10" @@ -938,6 +960,40 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +[[package]] +name = "rust-embed" +version = "8.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb44e1917075637ee8c7bcb865cf8830e3a92b5b1189e44e3a0ab5a0d5be314b" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382499b49db77a7c19abd2a574f85ada7e9dbe125d5d1160fa5cad7c4cf71fc9" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn 2.0.107", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21fcbee55c2458836bcdbfffb6ec9ba74bbc23ca7aa6816015a3dd2c4d8fc185" +dependencies = [ + "sha2", + "walkdir", +] + [[package]] name = "rustix" version = "1.1.2" @@ -957,6 +1013,15 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -990,7 +1055,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -1006,6 +1071,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_spanned" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +dependencies = [ + "serde_core", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1029,6 +1103,17 @@ dependencies = [ "digest", ] +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1037,12 +1122,15 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "sign" -version = "0.1.0" +version = "1.0.0" dependencies = [ "cc", "data-encoding", "ntex", + "rust-embed", "serde", + "thiserror", + "toml", ] [[package]] @@ -1093,9 +1181,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.106" +version = "2.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" dependencies = [ "proc-macro2", "quote", @@ -1119,9 +1207,48 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] +[[package]] +name = "toml" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" + [[package]] name = "tracing" version = "0.1.41" @@ -1158,7 +1285,7 @@ checksum = "41b6d82be61465f97d42bd1d15bf20f3b0a3a0905018f38f9d6f6962055b0b5c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -1167,6 +1294,16 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasip2" version = "1.0.1+wasi-0.2.4" @@ -1198,6 +1335,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -1292,6 +1438,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" + [[package]] name = "wit-bindgen" version = "0.46.0" @@ -1315,5 +1467,5 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] diff --git a/Cargo.toml b/Cargo.toml index e3e6890..a1330d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sign" -version = "1.0.0" +version = "1.1.0" edition = "2024" readme = "README.md" license = "AGPL-3.0-only" @@ -12,7 +12,10 @@ cc = "1.2.41" [dependencies] data-encoding = "2.9.0" ntex = { version = "2.16.0", features = ["compio"] } +rust-embed = "8.8.0" serde = { version = "1.0.228", features = ["derive"] } +thiserror = "2.0.17" +toml = "0.9.8" [profile.release] codegen-units = 1 diff --git a/README.md b/README.md index 4717640..18a0b7b 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This project can only be used on Linux. First, go to the official website to download QQ. -I recommend downloading the Linux x64 3.2.19-39038 version. If you need to download other versions, you will need to modify the `src/main.rs` file. +I recommend downloading the Linux x64 3.2.19-39038 version. If you want use another version, go to visit [Config section](#config). Then unzip or install QQ. @@ -21,6 +21,14 @@ Place the `libsymbols.so` and `target/release/sign` files into the folder that c Switch the directory to the folder containing `wrapper.node`, and then run `./sign`. -The server will listen on `127.0.0.1:8080`. If you want to listen on other endpoints, please go to modify the `src/main.rs` file. +The server will listen on `127.0.0.1:8080`. If you want to listen on other endpoints, go to modify the `src/main.rs` file. Enjoy! + +## Config + +Copy the `sign.config.toml` file to the folder where the `wrapper.node` is located. + +Then modify the configuration file according to your needs. + +If you use other versions of QQ, put `{version}.json` in the same directory. For specific content, refer to `src/appinfo/3.2.19-39038.json`. diff --git a/sign.config.toml b/sign.config.toml new file mode 100644 index 0000000..217fa81 --- /dev/null +++ b/sign.config.toml @@ -0,0 +1,4 @@ +preloads = ["libgnutls.so.30", "./libsymbols.so"] +listen = "127.0.0.1:8080" +offset = 0x5ADE220 +version = "3.2.19-39038" diff --git a/src/appinfo/3.2.19-39038.json b/src/appinfo/3.2.19-39038.json new file mode 100644 index 0000000..8350c17 --- /dev/null +++ b/src/appinfo/3.2.19-39038.json @@ -0,0 +1,18 @@ +{ + "Os": "Linux", + "VendorOs": "linux", + "Kernel": "Linux", + "CurrentVersion": "3.2.19-39038", + "MiscBitmap": 32764, + "PtVersion": "2.0.0", + "SsoVersion": 19, + "PackageName": "com.tencent.qq", + "WtLoginSdk": "nt.wtlogin.0.0.1", + "AppId": 1600001615, + "SubAppId": 537313942, + "AppIdQrCode": 537313942, + "AppClientVersion": 39038, + "MainSigMap": 169742560, + "SubSigMap": 0, + "NTLoginType": 1 +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..c9ef245 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Copyright (C) 2025 Moew72 + +use std::fs; +use std::io::Read; +use std::sync::LazyLock; + +use serde::Deserialize; + +pub(crate) static CONFIG: LazyLock = LazyLock::new(|| { + if fs::exists("sign.config.toml").unwrap() { + let mut buf = Vec::new(); + fs::File::open("sign.config.toml") + .unwrap() + .read_to_end(&mut buf) + .unwrap(); + toml::from_slice::(&buf).unwrap() + } else { + Config::default() + } +}); + +#[derive(Debug, Deserialize)] +pub(crate) struct Config { + pub(crate) preloads: Vec, + pub(crate) listen: String, + pub(crate) offset: usize, + pub(crate) version: String, +} + +impl Default for Config { + fn default() -> Self { + Self { + preloads: vec!["libgnutls.so.30".to_string(), "./libsymbols.so".to_string()], + listen: "127.0.0.1:8080".to_string(), + offset: 0x5ADE220, + version: "3.2.19-39038".to_string(), + } + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..393fd29 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Copyright (C) 2025 Moew72 + +use ntex::web; +use serde::Serialize; + +#[derive(thiserror::Error, Debug)] +pub(crate) enum Error { + #[error("std io: `{0}`")] + Io(#[from] std::io::Error), + #[error("from utf8: `{0}`")] + FromUtf8(#[from] std::string::FromUtf8Error), + #[error("data_encoding: `{0}`")] + DataEncoding(#[from] data_encoding::DecodeError), + #[error("ntex runtime JoinHandle: `{0}`")] + NtexRuntimeJoinHandle(#[from] ntex::rt::JoinError), + #[error("other: `{0}`")] + Other(String), +} + +pub type Result = std::result::Result; + +pub(crate) trait Context { + fn context(self, ctx: Error) -> Result; +} + +impl Context for Option { + fn context(self, ctx: Error) -> Result { + match self { + Some(v) => Ok(v), + None => Err(ctx), + } + } +} + +impl web::WebResponseError for Error { + fn error_response(&self, _: &ntex::web::HttpRequest) -> ntex::http::Response { + #[derive(Debug, Serialize)] + struct RespBody { + status: u16, + message: String, + } + web::HttpResponse::build(self.status_code()).json(&RespBody { + status: 400, + message: self.to_string(), + }) + } +} diff --git a/src/main.rs b/src/main.rs index 99fcf69..9e63525 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,127 +1,35 @@ // SPDX-License-Identifier: AGPL-3.0-only // Copyright (C) 2025 Moew72 -use std::ffi::{CString, c_char}; -use std::mem::ManuallyDrop; -use std::ptr::null; +mod config; +mod error; +mod service; +mod sign; -use data_encoding::HEXUPPER; use ntex::web; -use serde::{Deserialize, Serialize}; -mod lib { - use std::ffi::*; - type Func = extern "C" fn(*const c_char, *const c_uchar, c_int, c_int, *mut c_uchar); - unsafe extern "C" { - pub(super) static mut libs: *mut *const c_char; - pub(super) static mut offset: usize; - pub(super) static mut sign: Func; - pub(super) fn load_module() -> c_int; - pub(super) fn unload_module(); - } -} - -fn set_libs(libs: Vec<&str>) { - let mut libs = libs - .iter() - .map(|&x| ManuallyDrop::new(CString::new(x).unwrap()).as_ptr()) - .collect::>(); - libs.push(null()); - unsafe { - lib::libs = ManuallyDrop::new(libs).as_mut_ptr(); - } -} - -fn set_offset(offset: usize) { - unsafe { - lib::offset = offset; - } -} - -fn load_module() { - let ret = unsafe { lib::load_module() }; - if ret != 0 { - panic!("load module error."); - } -} - -#[allow(unused)] -fn unload_module() { - unsafe { lib::unload_module() } -} - -fn sign(cmd: &str, src: &[u8], seq: i32) -> [Vec; 3] { - const TOKEN_DATA_OFFSET: usize = 0x000; - const TOKEN_LEN_OFFSET: usize = 0x0FF; - const EXTRA_DATA_OFFSET: usize = 0x100; - const EXTRA_LEN_OFFSET: usize = 0x1FF; - const SIGN_DATA_OFFSET: usize = 0x200; - const SIGN_LEN_OFFSET: usize = 0x2FF; - - let c_cmd = CString::new(cmd).unwrap(); - let mut buf = [0u8; 0x300]; - let _ = unsafe { - lib::sign( - c_cmd.as_ptr(), - src.as_ptr(), - src.len() as i32, - seq, - buf.as_mut_ptr(), - ) - }; - - let token_len = buf[TOKEN_LEN_OFFSET]; - let token = &buf[TOKEN_DATA_OFFSET..TOKEN_DATA_OFFSET + token_len as usize]; - let extra_len = buf[EXTRA_LEN_OFFSET]; - let extra = &buf[EXTRA_DATA_OFFSET..EXTRA_DATA_OFFSET + extra_len as usize]; - let sign_len = buf[SIGN_LEN_OFFSET]; - let sign = &buf[SIGN_DATA_OFFSET..SIGN_DATA_OFFSET + sign_len as usize]; - - [Vec::from(token), Vec::from(extra), Vec::from(sign)] -} +use crate::{ + config::CONFIG, + sign::{load_module, set_libs, set_offset, unload_module}, +}; #[ntex::main] async fn main() -> std::io::Result<()> { - set_libs(vec!["libgnutls.so.30", "./libsymbols.so"]); - set_offset(0x5ADE220); + set_libs(CONFIG.preloads.clone()); + set_offset(CONFIG.offset); load_module(); - web::HttpServer::new(|| web::App::new().service(sign_service)) - .bind(("127.0.0.1", 8080))? - .run() - .await -} -#[web::post("/")] -async fn sign_service(params: web::types::Json) -> impl web::Responder { - let ret = HEXUPPER.decode(params.src.to_uppercase().as_bytes()); - let src = match ret { - Ok(v) => v, - Err(err) => return web::HttpResponse::InternalServerError().body(err.to_string()), - }; - let [token, extra, sign] = sign(¶ms.cmd, &src, params.seq); - let token = HEXUPPER.encode(&token); - let extra = HEXUPPER.encode(&extra); - let sign = HEXUPPER.encode(&sign); - let value = Value { token, extra, sign }; - let body = RespBody { value }; - web::HttpResponse::Ok().json(&body) -} + web::HttpServer::new(|| { + web::App::new() + .service(service::sign_get) + .service(service::sign_post) + .service(service::appinfo) + }) + .bind(CONFIG.listen.clone())? + .run() + .await?; -#[derive(Deserialize)] -struct Params { - cmd: String, - src: String, - seq: i32, -} + unload_module(); -#[derive(Serialize)] -struct RespBody { - value: Value, -} - -#[derive(Serialize)] -struct Value { - token: String, - extra: String, - sign: String, + Ok(()) } diff --git a/src/service.rs b/src/service.rs new file mode 100644 index 0000000..292cb32 --- /dev/null +++ b/src/service.rs @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Copyright (C) 2025 Moew72 + +use std::fs; +use std::io::Read; + +use data_encoding::HEXUPPER; +use ntex::http::Response; +use ntex::http::StatusCode; +use ntex::http::header::CONTENT_TYPE; +use ntex::rt::spawn; +use ntex::web; +use serde::{Deserialize, Serialize}; + +use crate::config::CONFIG; +use crate::error::{Context, Error, Result}; + +#[derive(rust_embed::Embed)] +#[folder = "src/appinfo"] +struct Asset; + +#[derive(Deserialize)] +struct Params { + cmd: String, + src: String, + seq: i32, +} + +#[web::get("/")] +pub(crate) async fn sign_get(params: web::types::Query) -> Result { + sign_common(params.0).await +} + +#[web::post("/")] +pub(crate) async fn sign_post(params: web::types::Json) -> Result { + sign_common(params.0).await +} + +#[inline] +async fn sign_common(params: Params) -> Result { + #[derive(Serialize)] + struct RespBody { + platform: String, + version: String, + value: Value, + } + #[derive(Serialize)] + struct Value { + token: String, + extra: String, + sign: String, + } + let src = HEXUPPER.decode(params.src.to_uppercase().as_bytes())?; + let [token, extra, sign] = + spawn(async move { crate::sign::sign(¶ms.cmd, &src, params.seq) }).await?; + let token = HEXUPPER.encode(&token); + let extra = HEXUPPER.encode(&extra); + let sign = HEXUPPER.encode(&sign); + let value = Value { token, extra, sign }; + let body = RespBody { + platform: "Linux".to_string(), + value, + version: CONFIG.version.clone(), + }; + let resp = web::HttpResponse::Ok().json(&body); + Ok(resp) +} + +#[web::get("/appinfo")] +pub(crate) async fn appinfo() -> Result { + let path = &format!("{}.json", CONFIG.version.clone()); + let body = if fs::exists(path)? { + let mut buf = String::new(); + fs::File::open(path)?.read_to_string(&mut buf)?; + buf + } else { + let data = Asset::get(path) + .context(Error::Other(format!("appinfo {} not found.", path)))? + .data + .to_vec(); + String::from_utf8(data)? + }; + let resp = web::HttpResponse::build(StatusCode::OK) + .set_header(CONTENT_TYPE, "application/json") + .body(body); + Ok(resp) +} diff --git a/src/sign.rs b/src/sign.rs new file mode 100644 index 0000000..7717f7d --- /dev/null +++ b/src/sign.rs @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: AGPL-3.0-only +// Copyright (C) 2025 Moew72 + +use std::ffi::{CString, c_char}; +use std::mem::ManuallyDrop; +use std::ptr::null; + +mod lib { + use std::ffi::*; + type Func = extern "C" fn(*const c_char, *const c_uchar, c_int, c_int, *mut c_uchar); + unsafe extern "C" { + pub(super) static mut libs: *mut *const c_char; + pub(super) static mut offset: usize; + pub(super) static mut sign: Func; + pub(super) fn load_module() -> c_int; + pub(super) fn unload_module(); + } +} + +pub(crate) fn set_libs(libs: Vec) { + let mut libs = libs + .iter() + .map(|x| ManuallyDrop::new(CString::new(x.as_str()).unwrap()).as_ptr()) + .collect::>(); + libs.push(null()); + unsafe { + lib::libs = ManuallyDrop::new(libs).as_mut_ptr(); + } +} + +pub(crate) fn set_offset(offset: usize) { + unsafe { + lib::offset = offset; + } +} + +pub(crate) fn load_module() { + let ret = unsafe { lib::load_module() }; + if ret != 0 { + panic!("load module error."); + } +} + +#[allow(unused)] +pub(crate) fn unload_module() { + unsafe { lib::unload_module() } +} + +pub(crate) fn sign(cmd: &str, src: &[u8], seq: i32) -> [Vec; 3] { + const TOKEN_DATA_OFFSET: usize = 0x000; + const TOKEN_LEN_OFFSET: usize = 0x0FF; + const EXTRA_DATA_OFFSET: usize = 0x100; + const EXTRA_LEN_OFFSET: usize = 0x1FF; + const SIGN_DATA_OFFSET: usize = 0x200; + const SIGN_LEN_OFFSET: usize = 0x2FF; + + let c_cmd = CString::new(cmd).unwrap(); + let mut buf = [0u8; 0x300]; + let _ = unsafe { + lib::sign( + c_cmd.as_ptr(), + src.as_ptr(), + src.len() as i32, + seq, + buf.as_mut_ptr(), + ) + }; + + let token_len = buf[TOKEN_LEN_OFFSET]; + let token = &buf[TOKEN_DATA_OFFSET..TOKEN_DATA_OFFSET + token_len as usize]; + let extra_len = buf[EXTRA_LEN_OFFSET]; + let extra = &buf[EXTRA_DATA_OFFSET..EXTRA_DATA_OFFSET + extra_len as usize]; + let sign_len = buf[SIGN_LEN_OFFSET]; + let sign = &buf[SIGN_DATA_OFFSET..SIGN_DATA_OFFSET + sign_len as usize]; + + [Vec::from(token), Vec::from(extra), Vec::from(sign)] +}