├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── examples └── decode_simple_base64.rs └── src ├── lib.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea 3 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.7.18" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "aleka" 16 | version = "0.1.0" 17 | dependencies = [ 18 | "base64", 19 | "clap", 20 | "env_logger", 21 | "hex", 22 | "log", 23 | "serde", 24 | "serde_json", 25 | ] 26 | 27 | [[package]] 28 | name = "atty" 29 | version = "0.2.14" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 32 | dependencies = [ 33 | "hermit-abi", 34 | "libc", 35 | "winapi", 36 | ] 37 | 38 | [[package]] 39 | name = "autocfg" 40 | version = "1.1.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 43 | 44 | [[package]] 45 | name = "base64" 46 | version = "0.13.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" 49 | 50 | [[package]] 51 | name = "bitflags" 52 | version = "1.3.2" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 55 | 56 | [[package]] 57 | name = "cfg-if" 58 | version = "1.0.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 61 | 62 | [[package]] 63 | name = "clap" 64 | version = "3.1.18" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" 67 | dependencies = [ 68 | "atty", 69 | "bitflags", 70 | "clap_derive", 71 | "clap_lex", 72 | "indexmap", 73 | "lazy_static", 74 | "strsim", 75 | "termcolor", 76 | "textwrap", 77 | ] 78 | 79 | [[package]] 80 | name = "clap_derive" 81 | version = "3.1.18" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c" 84 | dependencies = [ 85 | "heck", 86 | "proc-macro-error", 87 | "proc-macro2", 88 | "quote", 89 | "syn", 90 | ] 91 | 92 | [[package]] 93 | name = "clap_lex" 94 | version = "0.2.0" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" 97 | dependencies = [ 98 | "os_str_bytes", 99 | ] 100 | 101 | [[package]] 102 | name = "env_logger" 103 | version = "0.9.0" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" 106 | dependencies = [ 107 | "atty", 108 | "humantime", 109 | "log", 110 | "regex", 111 | "termcolor", 112 | ] 113 | 114 | [[package]] 115 | name = "hashbrown" 116 | version = "0.11.2" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 119 | 120 | [[package]] 121 | name = "heck" 122 | version = "0.4.0" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 125 | 126 | [[package]] 127 | name = "hermit-abi" 128 | version = "0.1.19" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 131 | dependencies = [ 132 | "libc", 133 | ] 134 | 135 | [[package]] 136 | name = "hex" 137 | version = "0.4.3" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 140 | 141 | [[package]] 142 | name = "humantime" 143 | version = "2.1.0" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 146 | 147 | [[package]] 148 | name = "indexmap" 149 | version = "1.8.1" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" 152 | dependencies = [ 153 | "autocfg", 154 | "hashbrown", 155 | ] 156 | 157 | [[package]] 158 | name = "itoa" 159 | version = "1.0.1" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" 162 | 163 | [[package]] 164 | name = "lazy_static" 165 | version = "1.4.0" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 168 | 169 | [[package]] 170 | name = "libc" 171 | version = "0.2.125" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" 174 | 175 | [[package]] 176 | name = "log" 177 | version = "0.4.17" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 180 | dependencies = [ 181 | "cfg-if", 182 | ] 183 | 184 | [[package]] 185 | name = "memchr" 186 | version = "2.5.0" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 189 | 190 | [[package]] 191 | name = "os_str_bytes" 192 | version = "6.0.1" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435" 195 | 196 | [[package]] 197 | name = "proc-macro-error" 198 | version = "1.0.4" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 201 | dependencies = [ 202 | "proc-macro-error-attr", 203 | "proc-macro2", 204 | "quote", 205 | "syn", 206 | "version_check", 207 | ] 208 | 209 | [[package]] 210 | name = "proc-macro-error-attr" 211 | version = "1.0.4" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 214 | dependencies = [ 215 | "proc-macro2", 216 | "quote", 217 | "version_check", 218 | ] 219 | 220 | [[package]] 221 | name = "proc-macro2" 222 | version = "1.0.38" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa" 225 | dependencies = [ 226 | "unicode-xid", 227 | ] 228 | 229 | [[package]] 230 | name = "quote" 231 | version = "1.0.18" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" 234 | dependencies = [ 235 | "proc-macro2", 236 | ] 237 | 238 | [[package]] 239 | name = "regex" 240 | version = "1.5.5" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" 243 | dependencies = [ 244 | "aho-corasick", 245 | "memchr", 246 | "regex-syntax", 247 | ] 248 | 249 | [[package]] 250 | name = "regex-syntax" 251 | version = "0.6.25" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 254 | 255 | [[package]] 256 | name = "ryu" 257 | version = "1.0.9" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" 260 | 261 | [[package]] 262 | name = "serde" 263 | version = "1.0.137" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" 266 | dependencies = [ 267 | "serde_derive", 268 | ] 269 | 270 | [[package]] 271 | name = "serde_derive" 272 | version = "1.0.137" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" 275 | dependencies = [ 276 | "proc-macro2", 277 | "quote", 278 | "syn", 279 | ] 280 | 281 | [[package]] 282 | name = "serde_json" 283 | version = "1.0.81" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" 286 | dependencies = [ 287 | "itoa", 288 | "ryu", 289 | "serde", 290 | ] 291 | 292 | [[package]] 293 | name = "strsim" 294 | version = "0.10.0" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 297 | 298 | [[package]] 299 | name = "syn" 300 | version = "1.0.94" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "a07e33e919ebcd69113d5be0e4d70c5707004ff45188910106854f38b960df4a" 303 | dependencies = [ 304 | "proc-macro2", 305 | "quote", 306 | "unicode-xid", 307 | ] 308 | 309 | [[package]] 310 | name = "termcolor" 311 | version = "1.1.3" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 314 | dependencies = [ 315 | "winapi-util", 316 | ] 317 | 318 | [[package]] 319 | name = "textwrap" 320 | version = "0.15.0" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" 323 | 324 | [[package]] 325 | name = "unicode-xid" 326 | version = "0.2.3" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" 329 | 330 | [[package]] 331 | name = "version_check" 332 | version = "0.9.4" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 335 | 336 | [[package]] 337 | name = "winapi" 338 | version = "0.3.9" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 341 | dependencies = [ 342 | "winapi-i686-pc-windows-gnu", 343 | "winapi-x86_64-pc-windows-gnu", 344 | ] 345 | 346 | [[package]] 347 | name = "winapi-i686-pc-windows-gnu" 348 | version = "0.4.0" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 351 | 352 | [[package]] 353 | name = "winapi-util" 354 | version = "0.1.5" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 357 | dependencies = [ 358 | "winapi", 359 | ] 360 | 361 | [[package]] 362 | name = "winapi-x86_64-pc-windows-gnu" 363 | version = "0.4.0" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 366 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aleka" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | base64 = "*" 10 | hex = "0.4.3" 11 | env_logger = "0.9.0" 12 | log = "0.4.17" 13 | clap = { version = "3.1.18", features = ["derive"] } 14 | serde = { version = "1.0.104", features = ["derive"] } 15 | serde_json = "1.0.48" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Aleka: a schema agnostic protobuf decoder tool 2 | 3 | Command line tool and library to decode base64/hex strings of binary protobuf messages 4 | and does its best to decode the contents according to the specification. 5 | 6 | The output is in JSON format 7 | 8 | https://developers.google.com/protocol-buffers/docs/encoding 9 | https://developers.google.com/protocol-buffers/docs/encoding#cheat-sheet 10 | 11 | Blog post about with some more details about Aleka [https://miguelabate.com/aleka-a-schema-agnostic-protobuf-decoder/](https://miguelabate.com/aleka-a-schema-agnostic-protobuf-decoder/) 12 | 13 | ## Usage 14 | 15 | cargo run -- --input-type hex --data 08f4ffffffffffffffff0110d2091a0768656c6c6f6f6f25713d0a422d5e0e0000321e08c80312076d696775656c3112076d696775656c3212076d696775656c3338d30342030f01cf48e58de7ef8c1d50015a1b09bc7f62010000000012076d696775656c3112076d696775656c325a21093f420f0000000000120a6d696775656c63636331120a6d696775656c63636332 16 | 17 | ## Structure of the output JSON 18 | 19 | The output json schema is: 20 | ``` 21 | //a proto message 22 | Message{ 23 | fields: array of Field 24 | } 25 | 26 | //representation of a field of the proto message, it could be a value, or repeated values, or a submessage or a list of submessages. 27 | //Values and message can be filled at the same time in case that the decoding is ambiguous and the result can be both a proto message and a string 28 | Field{ 29 | field_number: int expressing the field number in the proto 30 | values: array of Value 31 | messages: array of Message 32 | } 33 | 34 | //since we don't have a schema, a value can have different interpretations depending on how it's decoded. That's why there is a list here, to show the different possibilities 35 | Value{ 36 | value_representations: array of ValueRepresentation 37 | } 38 | 39 | ValueRepresentation{ 40 | value: the value as a String 41 | format_type: the type used to represent this value 42 | } 43 | ``` -------------------------------------------------------------------------------- /examples/decode_simple_base64.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use aleka::{decode_proto, Message}; 3 | 4 | fn main() -> Result<(),Box> { 5 | env_logger::init(); 6 | let decoded_proto = base64::decode("CgdNaWNoYWVsEBQdcT3qPyIETWlrZSIJVGhlIEJpZyBNKhYKC0Zha2UgU3RyZWV0EgMxMjMaAk5MKhsKDVJhbmRvbSBTdHJlZXQSBjExMjMzNBoCUFQ4xwE=").unwrap(); 7 | let message: Result> = decode_proto(&decoded_proto); 8 | 9 | let serialized = serde_json::to_string_pretty(&message?).unwrap(); 10 | println!("{}",serialized); 11 | 12 | Ok(()) 13 | } -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use serde::{Serialize, Deserialize}; 3 | use log::{debug, warn}; 4 | 5 | 6 | const TYPE_0:u8 = 0;//Varint- int32, int64, uint32, uint64, sint32, sint64, bool, enum 7 | const TYPE_1:u8 = 1;//64-bit- fixed64, sfixed64, double 8 | const TYPE_2:u8 = 2;//Length-delimited - string, bytes, embedded messages, packed repeated fields 9 | const TYPE_3:u8 = 3;//groups (deprecated) 10 | const TYPE_4:u8 = 4;//groups (deprecated) 11 | const TYPE_5:u8 = 5;//32-bit- fixed32, sfixed32, float 12 | 13 | 14 | #[derive(Debug, Deserialize, Serialize)] 15 | pub struct ValueRepresentation { 16 | pub value: String, 17 | pub format_type: String, 18 | } 19 | 20 | ///Represents a value of a field that is not a Message. Since each value can have many decodings depending on the type, we provide a list of possible represntations fo the value. 21 | #[derive(Debug, Deserialize, Serialize)] 22 | pub struct Value { 23 | pub value_representations: Vec, 24 | } 25 | 26 | ///Each field of the proto has a field number and can be either a value or another proto message. They are represented as lists becasue can be "repeated" 27 | #[derive(Debug, Deserialize, Serialize)] 28 | pub struct Field { 29 | pub field_number: i32, 30 | #[serde(skip_serializing_if = "std::vec::Vec::is_empty")] 31 | pub values: Vec, 32 | #[serde(skip_serializing_if = "std::vec::Vec::is_empty")] 33 | pub messages: Vec, 34 | } 35 | 36 | ///Text representation of the proto message 37 | /// A Message has a list of fields. 38 | #[derive(Debug, Deserialize, Serialize)] 39 | pub struct Message { 40 | pub fields: Vec, 41 | } 42 | 43 | impl Message { 44 | pub fn add_field(&mut self, field:Field){ 45 | for a_field in self.fields.iter_mut() { 46 | if a_field.field_number == field.field_number { 47 | field.values.into_iter().for_each(|a_val| {a_field.values.push(a_val)}); 48 | field.messages.into_iter().for_each(|a_msg| {a_field.messages.push(a_msg)}); 49 | return; 50 | } 51 | } 52 | self.fields.push(field); 53 | } 54 | } 55 | 56 | ///Represents the encoded key of the proto fields, composed by the field number and its type 57 | pub struct ProtoKey { 58 | pub field_number:i32, 59 | pub wire_type:u8, 60 | } 61 | 62 | impl ProtoKey { 63 | pub fn from_varint(a_raw_varint:&[u8]) -> Self { 64 | let decoded=decode_varint(a_raw_varint); 65 | let decoded_i32:i32 = decoded.get_value_as_i32(); 66 | ProtoKey{ field_number: decoded_i32 >> 3, wire_type: 0b0000_0111 & (decoded_i32 as u8) } 67 | } 68 | } 69 | 70 | impl ToString for ProtoKey { 71 | fn to_string(&self) -> String { 72 | format!("Field number: {} Wire type: {}",self.field_number, self.wire_type) 73 | } 74 | } 75 | 76 | ///Contains the bytes of the var int in the reverse order and with the first bit set to 0. 77 | #[derive(Debug)] 78 | pub struct VarIntDecodedData { 79 | pub value: Vec,//raw value of the varint bytes, for numbers, the most significant byte to the left 80 | } 81 | 82 | impl From for i32 { 83 | fn from(varint_decoded_data: VarIntDecodedData) -> Self { 84 | varint_decoded_data.get_value_as_i32() 85 | } 86 | } 87 | 88 | impl PartialEq for VarIntDecodedData { 89 | fn eq(&self, other: &i32) -> bool { 90 | *other==self.get_value_as_i32() 91 | } 92 | } 93 | impl PartialEq for i32 { 94 | fn eq(&self, other: &VarIntDecodedData) -> bool { 95 | other.get_value_as_i32()==*self 96 | } 97 | } 98 | 99 | impl VarIntDecodedData { 100 | pub fn get_value_as_i32(&self) -> i32 { 101 | let mut result:i32 = 0; 102 | for a_byte in self.value.iter() { 103 | result<<= 7; 104 | result |= *a_byte as i32; 105 | } 106 | result 107 | } 108 | 109 | pub fn get_value_as_signed_i32_zigzag(&self) -> i32 { 110 | let mut result:i32 = 0; 111 | for a_byte in self.value.iter() { 112 | result<<= 7; 113 | result |= *a_byte as i32; 114 | } 115 | return (result >> 1) ^ -(result & 1); 116 | } 117 | 118 | pub fn get_value_as_i64(&self) -> i64 { 119 | let mut result:i64 = 0; 120 | for a_byte in self.value.iter() { 121 | result<<= 7; 122 | result |= *a_byte as i64; 123 | } 124 | result 125 | } 126 | 127 | pub fn get_value_as_signed_i64_zigzag(&self) -> i64 { 128 | let mut result:i64 = 0; 129 | for a_byte in self.value.iter() { 130 | result<<= 7; 131 | result |= *a_byte as i64; 132 | } 133 | (result >> 1) ^ -(result & 1) 134 | } 135 | } 136 | 137 | 138 | pub fn decode_proto(decoded_proto:&[u8]) -> Result>{ 139 | let mut current_index:usize=0; 140 | let mut proto_message:Message=Message{ fields: vec![]}; 141 | 142 | while current_index { //int32, int64, uint32, uint64, sint32, sint64, bool, enum 148 | let (the_varint, offset) = get_varint_decoded_at(&decoded_proto, current_index); 149 | debug!("Value int32: {}", the_varint.get_value_as_i32()); 150 | debug!("Value zigzag decoded sint32: {}", the_varint.get_value_as_signed_i32_zigzag()); 151 | current_index += offset; 152 | //fill data in the message object 153 | proto_message.fields.push( 154 | Field{ field_number: key.field_number, 155 | values: vec![Value{ value_representations: vec![ValueRepresentation{ value: the_varint.get_value_as_i64().to_string(), format_type: "Varint(int32/int64/uint32/uint64/bool/enum)".to_owned() }, ValueRepresentation{ value: the_varint.get_value_as_signed_i64_zigzag().to_string(), format_type: "Varint(sint32/sint64)".to_owned() }] }], 156 | messages: vec![] 157 | } 158 | ); 159 | } 160 | TYPE_1 => { //expect a chunk of fixed 64 bits, fixed64, double(f64) sfixed64 161 | if decoded_proto.len()::try_from(&decoded_proto[current_index..current_index + 8])?; 166 | debug!("fixed64 value {}", i64::from_le_bytes(raw_data_bytes)); 167 | debug!("float64 value {}", f64::from_le_bytes(raw_data_bytes)); 168 | current_index += 8; 169 | //fill data in the message object 170 | proto_message.fields.push( 171 | Field{ field_number: key.field_number, 172 | values: vec![Value{ value_representations: vec![ValueRepresentation{ value: i64::from_le_bytes(raw_data_bytes).to_string(), format_type: "fixed64".to_owned() }, ValueRepresentation{ value: f64::from_le_bytes(raw_data_bytes).to_string(), format_type: "double".to_owned() }] }], 173 | messages: vec![] 174 | } 175 | ); 176 | } 177 | TYPE_2 => { //string, bytes or subobject 178 | let (data_length, offset) = get_varint_decoded_at(&decoded_proto, current_index); 179 | current_index += offset; 180 | 181 | if decoded_proto.len() { 226 | warn!("Not supported type"); 227 | return Err("Not supported type".into()); 228 | } 229 | TYPE_4 => { 230 | warn!("Not supported type"); 231 | return Err("Not supported type".into()); 232 | } 233 | TYPE_5 => { //expect a chunk of 32 bits (fixed32, sfixed32, float) 234 | if decoded_proto.len()::try_from(&decoded_proto[current_index..current_index + 4]).unwrap(); 239 | debug!("fixed32 value {}", i32::from_le_bytes(raw_data_bytes)); 240 | debug!("float value {}", f32::from_le_bytes(raw_data_bytes)); 241 | current_index += 4; 242 | //fill data in the message object 243 | proto_message.fields.push( 244 | Field{ field_number: key.field_number, 245 | values: vec![Value{ value_representations: vec![ValueRepresentation{ value: i32::from_le_bytes(raw_data_bytes).to_string(), format_type: "fixed32".to_owned() }, ValueRepresentation{ value: f32::from_le_bytes(raw_data_bytes).to_string(), format_type: "float".to_owned() }] }], 246 | messages: vec![] 247 | } 248 | ); 249 | } 250 | _ => { 251 | warn!("Invalid type: {}", key.wire_type); 252 | return Err("Invalid type".into()); 253 | } 254 | } 255 | } 256 | Ok(proto_message) 257 | } 258 | /** Returns the next varint decoded in the stream plus the amount of bytes read from the input. 259 | It assumes that the inmediate bytes are a varint*/ 260 | fn get_varint_decoded_at(a_byte_stream:&[u8], index:usize) -> (VarIntDecodedData, usize) { 261 | let result:Vec = get_varint_raw_at(a_byte_stream, index); 262 | let varint_found= decode_varint(&result); 263 | (varint_found,result.len()) 264 | } 265 | 266 | /**Returns the next varint in the stream plus the amount of bytes read from the input. 267 | It assumes that the inmediate bytes are a varint*/ 268 | fn get_varint_raw_at(a_byte_stream:&[u8], index:usize) -> Vec { 269 | let mut result:Vec = vec![]; 270 | for i in index .. a_byte_stream.len(){ 271 | if *a_byte_stream.get(i).unwrap() >> 7 == 1 { 272 | result.push(*a_byte_stream.get(i).unwrap()); 273 | }else{ 274 | result.push(*a_byte_stream.get(i).unwrap()); 275 | break; 276 | } 277 | }; 278 | result 279 | } 280 | 281 | fn decode_varint(a_varint:&[u8]) -> VarIntDecodedData { 282 | let mut temp_binarylist:Vec = vec![]; 283 | // let mut result:i32 = 0; 284 | //first take out the msb of each 285 | a_varint.iter().for_each(|a_byte| { 286 | let new_bte= a_byte & 0b0111_1111; 287 | // println!("0b{:08b}", new_bte); 288 | temp_binarylist.push( new_bte); 289 | }); 290 | temp_binarylist.reverse();//reverse the groups of 7 bits because varints store numbers with the least significant group first 291 | 292 | let decoded_data: VarIntDecodedData = VarIntDecodedData { value: temp_binarylist }; 293 | decoded_data 294 | } 295 | 296 | #[allow(dead_code)] 297 | fn encode_to_varint(a_number:i32) -> Vec { 298 | let mut result:Vec = vec![]; 299 | 300 | for i in 0 .. 5 { 301 | let mut new_byte:u8 = (a_number >> (7*i)) as u8; 302 | if i==4 { 303 | new_byte &= 0b0111_1111; // if it's the last byte, mark it with 0 at the beginning 304 | }else { 305 | new_byte |= 0b1000_0000; 306 | } 307 | // println!("0b{:08b}", new_byte); 308 | result.push(new_byte); 309 | } 310 | result 311 | } 312 | 313 | /// cargo test -- --show-output 314 | #[cfg(test)] 315 | mod tests { 316 | use crate::{decode_varint, VarIntDecodedData, encode_to_varint, get_varint_decoded_at, ProtoKey}; 317 | 318 | #[test] 319 | fn decode_varint_300() { 320 | let a_varint:Vec = vec![0b1010_1100, 0b0000_0010];// 300 codified as varint 321 | 322 | let an_int: VarIntDecodedData = decode_varint(&a_varint); 323 | assert_eq!(300, an_int.get_value_as_i32()); 324 | } 325 | 326 | #[test] 327 | fn encode_varint_1984() { 328 | let expected_result:Vec = vec![0b11000000, 0b10001111, 0b10000000, 0b10000000, 0b00000000];// 1984 codified as varint 329 | 330 | let result:Vec = encode_to_varint(1984); 331 | assert_eq!(expected_result, result); 332 | } 333 | 334 | #[test] 335 | fn encode_and_then_decode() { 336 | let an_int2: VarIntDecodedData = decode_varint( &encode_to_varint(-1984)); 337 | assert_eq!(-1984, an_int2.get_value_as_i32()); 338 | } 339 | 340 | #[test] 341 | fn get_varint_at_different_positions() { 342 | let mut index:usize = 0; 343 | let a_list_of_varint:Vec = vec![0b1010_1100, 0b0000_0010, 0b11000000, 0b10001111, 0b10000000, 0b10000000, 0b00000000,0b00001100,0b11010010,0b00001001];//300, 1984, 12, 1234 344 | 345 | let mut result = get_varint_decoded_at(&a_list_of_varint, index); 346 | assert_eq!(300, result.0); 347 | 348 | index+=result.1; 349 | result = get_varint_decoded_at(&a_list_of_varint, index); 350 | assert_eq!(1984, result.0); 351 | 352 | index+=result.1; 353 | result = get_varint_decoded_at(&a_list_of_varint, index); 354 | assert_eq!(12, result.0); 355 | 356 | index+=result.1; 357 | result = get_varint_decoded_at(&a_list_of_varint, index); 358 | assert_eq!(1234, result.0); 359 | 360 | } 361 | 362 | #[test] 363 | fn proto_key_create() { 364 | let mut a_key_varint:Vec = vec![0b00001000]; 365 | let mut the_key =ProtoKey::from_varint(&a_key_varint); 366 | assert_eq!(1, the_key.field_number); 367 | assert_eq!(0, the_key.wire_type); 368 | 369 | a_key_varint = vec![0b00010000]; 370 | the_key=ProtoKey::from_varint(&a_key_varint); 371 | assert_eq!(2, the_key.field_number); 372 | assert_eq!(0, the_key.wire_type); 373 | 374 | a_key_varint = vec![0b00011010]; 375 | the_key=ProtoKey::from_varint(&a_key_varint); 376 | assert_eq!(3, the_key.field_number); 377 | assert_eq!(2, the_key.wire_type); 378 | } 379 | } -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use clap::ArgEnum; 3 | use log::{info}; 4 | use std::error::Error; 5 | use aleka::{decode_proto, Message}; 6 | 7 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ArgEnum)] 8 | enum InputType { 9 | Hex, 10 | B64, 11 | } 12 | 13 | #[derive(Parser)] 14 | #[clap(author, version, about, long_about = None)] 15 | struct Args { 16 | /// Input type 17 | #[clap(short, long, arg_enum, default_value_t = InputType::B64)] 18 | input_type: InputType, 19 | 20 | /// Input data 21 | #[clap(short, long)] 22 | data: String, 23 | } 24 | 25 | fn main() -> Result<(),Box>{ //() is the empty tuple, similar to void, unit type . occupies no memory 26 | env_logger::init(); 27 | let args = Args::parse(); 28 | info!("Protobuf decoder tool"); 29 | 30 | let decoded_proto:Vec = if args.input_type == InputType::Hex { 31 | hex::decode(args.data).unwrap() 32 | }else{ 33 | base64::decode(args.data).unwrap() 34 | }; 35 | 36 | let message: Result> = decode_proto(&decoded_proto); 37 | 38 | let serialized = serde_json::to_string_pretty(&message?).unwrap(); 39 | println!("{}",serialized); 40 | 41 | Ok(()) 42 | 43 | } 44 | --------------------------------------------------------------------------------