0

I need to deserialize the following payload, in particular the aud field from a JSON object:

claims: Claims { 
    aud: One("CBr3zBlrKBbwmxOAM1avZQ=="), // 24 len
    // ...
}

claims::aud is an Aud enum:

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(untagged)]
pub enum Aud {
    One(String),
    Many(Vec<String>),
}

When I attempt to get a String from serde_json::to_string() it returns two additional characters, an opening and ending char.

use serde_json::Result;

fn aud_from_json(data: &claims::Aud) -> Result<String> {
    let v = serde_json::to_string(&data)?;
    Ok(v.to_owned())
}
let aud = aud_from_json(&token_data.claims.aud)?;
log::debug!("aud field: {:?}\t aud field len({})", &aud, &aud.len());
$ aud field: "\"CBr3zBlrKBbwmxOAM1avZQ==\""         aud field len(26)

It appears there may be trailing escape characters serialized into the string.
Is there a method that can return aud field returned as String, sanitized without extra characters?
e.g. "CBr3zBlrKBbwmxOAM1avZQ=="

Alex
  • 367
  • 2
  • 4
  • 14
  • It's hard to answer your question because it doesn't include a [MRE]. We can't tell what crates (and their versions), types, traits, fields, etc. are present in the code. It would make it easier for us to help you if you try to reproduce your error on the [Rust Playground](https://play.rust-lang.org) if possible, otherwise in a brand new Cargo project, then [edit] your question to include the additional info. There are [Rust-specific MRE tips](//stackoverflow.com/tags/rust/info) you can use to reduce your original code for posting here. Thanks! – Shepmaster Sep 30 '19 at 16:39
  • You also have some poorly named functions which might be underlying some of your problems. For example, you have a function called `aud_from_json` which does not take JSON as an argument. Instead, it seems to **create** JSON. – Shepmaster Sep 30 '19 at 16:40

2 Answers2

1

The issue was a situation where serde_json::to_string() was mistaken for an actual to_string() implementation, as opposed to a JSON string.

Extracting an inner member of an enum variant is better done by pattern-matching. Due to the OP's requirements, the last aud member of the Vec is the most important one

Final implementation (playground):

use std::io::Result;
use serde;
#[macro_use] extern crate serde_derive;

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(untagged)]
pub enum Aud {
    One(String),
    Many(Vec<String>),
}

fn aud_from_json(data: &Aud) -> Result<String> {
    match data {
        Aud::One(audience) => Ok(audience.clone()),
        Aud::Many(audiences) => audiences
          .last()
          .ok_or(std::io::Error::new(std::io::ErrorKind::NotFound, "No audience found"))
          .map(|r| r.clone())
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Sébastien Renauld
  • 19,203
  • 2
  • 46
  • 66
  • His issue is about the "extra escape characters serialized in the string", which you don't address at all – Jmb Sep 30 '19 at 09:38
  • Added a disclaimer, although I honestly don't see how that could be the issue. That is literally standard `Debug` behavior. – Sébastien Renauld Sep 30 '19 at 09:43
  • @SébastienRenauld, thanks for the clarification. I do need to parse the string without the quotes since I need to decrypt the `aud` field. I may have to do some more digging on `Deserialize` trait as you have pointed out or parse them out manually. I thought perhaps serde-json had a convenience method to handle this case, that I was unaware of. – Alex Sep 30 '19 at 13:58
  • @Alex I get the feeling you're using the wrong tool for the job. You're provided with the base64-encoded string provided in your question as an example, correct? Is this part of a JWT? If so, where and how is JSON involved? – Sébastien Renauld Sep 30 '19 at 14:04
  • @SébastienRenauld `aud` is being used as an encrypted audience server identifier that is injected to a token as base64. This is just an added layer of obfuscation on top of Atlassian S2S authentication protocol specification. I'd like to de-structure `aud` from a given token response and decrypt it's value as an inspect method. – Alex Sep 30 '19 at 14:28
  • The encrypt/decrypt methods are spawned through a thread. Right now, I'm getting an error: `thread 'main' panicked at 'called Result::unwrap() on an Err value: Base64Error(InvalidByte(0, 34))` which I presume is because of the Quote characters. – Alex Sep 30 '19 at 14:32
  • @Alex Okay. Let's go back to first principles. Do you already have your `Claims` object deserialized? If so, what does your thread expect? A `String`? JSON? If it's a `String`, what prevents you from skipping the JSON serialization altogether? The reason I think you may be using the wrong tool is because I'm missing the point of JSON-serializing "just" a string. – Sébastien Renauld Sep 30 '19 at 14:40
  • @SébastienRenauld I'm sorry for the confusion. I need to deserialize the aud to a String. To add more context on the reason as to why I'm spawning a thread for this is because I'm using a thread-safe cell to protect a static string key which is used in the obfuscation of aud to AES-256 base64 encoding. – Alex Sep 30 '19 at 16:32
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/200181/discussion-between-sebastien-renauld-and-alex). – Sébastien Renauld Sep 30 '19 at 16:36
0

If I understood correctly what you said in reply to @SébastienRenauld's answer, you want to base-64 decode the string that's contained in an Aud instance. This can be achieved with:

let aud = Aud::One("CBr3zBlrKBbwmxOAM1avZQ==".to_string());
if let Aud::One(code) = aud {
    let decoded = base64::decode(&code).unwrap();
    log::debug!("Decoded: {:?}", decoded);
}

playground

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Jmb
  • 18,893
  • 2
  • 28
  • 55
  • Not exactly, `aud` is an AES 256-bit base64 encoded field. I'm going to be using a secret key in a wrapper method which will call `decrypt_base64_to_string(aud: &str)`. – Alex Sep 30 '19 at 15:55