//! JSON Web Signature (JWS) represents content secured with digital signatures or Message Authentication Codes (MACs) using JSON-based data structures.
//!
//! See [RFC7515](https://tools.ietf.org/html/rfc7515).

use crate::hash::HashAlgorithm;
use crate::jose::jwk::Jwk;
use crate::key::{EcCurve, PrivateKey, PublicKey};
use crate::signature::{SignatureAlgorithm, SignatureError};
use base64::engine::general_purpose;
use base64::{DecodeError, Engine as _};
use picky_asn1::wrapper::IntegerAsn1;
use picky_asn1_x509::signature::EcdsaSignatureValue;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::collections::HashMap;
use thiserror::Error;

// === error type === //

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum JwsError {
    /// RSA error
    #[error("RSA error: {context}")]
    Rsa { context: String },

    /// Json error
    #[error("JSON error: {source}")]
    Json { source: serde_json::Error },

    /// signature error
    #[error("signature error: {source}")]
    Signature { source: SignatureError },

    /// invalid token encoding
    #[error("input isn't a valid token string: {input}")]
    InvalidEncoding { input: String },

    /// couldn't decode base64
    #[error("couldn't decode base64: {source}")]
    Base64Decoding { source: DecodeError },

    /// input isn't valid utf8
    #[error("input isn't valid utf8: {source}, input: {input:?}")]
    InvalidUtf8 {
        source: std::string::FromUtf8Error,
        input: Vec<u8>,
    },
}

impl From<rsa::errors::Error> for JwsError {
    fn from(e: rsa::errors::Error) -> Self {
        Self::Rsa { context: e.to_string() }
    }
}

impl From<serde_json::Error> for JwsError {
    fn from(e: serde_json::Error) -> Self {
        Self::Json { source: e }
    }
}

impl From<SignatureError> for JwsError {
    fn from(e: SignatureError) -> Self {
        Self::Signature { source: e }
    }
}

impl From<DecodeError> for JwsError {
    fn from(e: DecodeError) -> Self {
        Self::Base64Decoding { source: e }
    }
}

// === JWS algorithms === //

mod jws_alg {
    // We use #[deprecated] for `JwsAlg::ED22519` to tell users that this algorithm is invalid
    // (`JwsAlg::EdDSA` should be used instead) but we should keep it inside enum to allow decoding
    // of invalid tokens generated by other libraries such as `golang-jws`. However, to avoid
    // compilation warnings caused by enum derives, we need to allow deprecated items inside this
    // module.
    #![allow(deprecated)]

    use super::*;

    /// `alg` header parameter values for JWS
    ///
    /// [JSON Web Algorithms (JWA) draft-ietf-jose-json-web-algorithms-40 #3](https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-3.1)
    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
    pub enum JwsAlg {
        /// HMAC using SHA-256 (unsupported)
        ///
        /// Required by RFC
        HS256,

        /// HMAC using SHA-384 (unsupported)
        HS384,

        /// HMAC using SHA-512 (unsupported)
        HS512,

        /// RSASSA-PKCS-v1_5 using SHA-256
        ///
        /// Recommended by RFC
        RS256,

        /// RSASSA-PKCS-v1_5 using SHA-384
        RS384,

        /// RSASSA-PKCS-v1_5 using SHA-512
        RS512,

        /// ECDSA using P-256 and SHA-256
        ///
        /// Recommended+ by RFC
        ES256,

        /// ECDSA using P-384 and SHA-384
        ES384,

        /// ECDSA using P-521 and SHA-512
        ES512,

        /// RSASSA-PSS using SHA-256 and MGF1 with SHA-256 (unsupported)
        PS256,

        /// RSASSA-PSS using SHA-384 and MGF1 with SHA-384 (unsupported)
        PS384,

        /// RSASSA-PSS using SHA-512 and MGF1 with SHA-512 (unsupported)
        PS512,

        /// EdDSA using Ed25519/Ed448
        EdDSA,

        /// [DO NOT USE] EdDSA using Ed25519
        ///
        /// This value is used by some popular libraries (e.g. `golang-jwt) instead of `EdDSA` due to
        /// mistake in the implementation. This value is deprecated and should not be used.
        #[deprecated(note = "You should not use this value, but it may appear in the wild")]
        ED25519,
    }
}
// Hack to localize #![allow(deprecated)]
pub use jws_alg::JwsAlg;

impl TryFrom<SignatureAlgorithm> for JwsAlg {
    type Error = SignatureError;

    fn try_from(v: SignatureAlgorithm) -> Result<Self, Self::Error> {
        match v {
            SignatureAlgorithm::RsaPkcs1v15(HashAlgorithm::SHA2_256) => Ok(Self::RS256),
            SignatureAlgorithm::RsaPkcs1v15(HashAlgorithm::SHA2_384) => Ok(Self::RS384),
            SignatureAlgorithm::RsaPkcs1v15(HashAlgorithm::SHA2_512) => Ok(Self::RS512),
            SignatureAlgorithm::Ecdsa(HashAlgorithm::SHA2_256) => Ok(Self::ES256),
            SignatureAlgorithm::Ecdsa(HashAlgorithm::SHA2_384) => Ok(Self::ES384),
            SignatureAlgorithm::Ecdsa(HashAlgorithm::SHA2_512) => Ok(Self::ES512),
            SignatureAlgorithm::Ed25519 => Ok(Self::EdDSA),
            unsupported => Err(SignatureError::UnsupportedAlgorithm {
                algorithm: format!("{unsupported:?}"),
            }),
        }
    }
}

impl TryFrom<JwsAlg> for SignatureAlgorithm {
    type Error = SignatureError;

    fn try_from(v: JwsAlg) -> Result<Self, Self::Error> {
        match v {
            JwsAlg::RS256 => Ok(SignatureAlgorithm::RsaPkcs1v15(HashAlgorithm::SHA2_256)),
            JwsAlg::RS384 => Ok(SignatureAlgorithm::RsaPkcs1v15(HashAlgorithm::SHA2_384)),
            JwsAlg::RS512 => Ok(SignatureAlgorithm::RsaPkcs1v15(HashAlgorithm::SHA2_512)),
            JwsAlg::ES256 => Ok(SignatureAlgorithm::Ecdsa(HashAlgorithm::SHA2_256)),
            JwsAlg::ES384 => Ok(SignatureAlgorithm::Ecdsa(HashAlgorithm::SHA2_384)),
            JwsAlg::ES512 => Ok(SignatureAlgorithm::Ecdsa(HashAlgorithm::SHA2_512)),
            JwsAlg::EdDSA => Ok(SignatureAlgorithm::Ed25519),
            #[allow(deprecated)]
            JwsAlg::ED25519 => Ok(SignatureAlgorithm::Ed25519),
            unsupported => Err(SignatureError::UnsupportedAlgorithm {
                algorithm: format!("{unsupported:?}"),
            }),
        }
    }
}

// === JWS header === //

/// JOSE header of a JWS
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct JwsHeader {
    // -- specific to JWS -- //
    /// Algorithm Header
    ///
    /// identifies the cryptographic algorithm used to secure the JWS.
    pub alg: JwsAlg,

    // -- common with JWE -- //
    /// JWK Set URL
    ///
    /// URI that refers to a resource for a set of JSON-encoded public keys,
    /// one of which corresponds to the key used to digitally sign the JWS.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub jku: Option<String>,

    /// JSON Web Key
    ///
    /// The public key that corresponds to the key used to digitally sign the JWS.
    /// This key is represented as a JSON Web Key (JWK).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub jwk: Option<Jwk>,

    /// Type header
    ///
    /// Used by JWS applications to declare the media type [IANA.MediaTypes] of this complete JWS.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub typ: Option<String>,

    /// Content Type header
    ///
    /// Used by JWS applications to declare the media type [IANA.MediaTypes] of the secured content (the payload).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cty: Option<String>,

    // -- common with all -- //
    /// Key ID Header
    ///
    /// A hint indicating which key was used.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub kid: Option<String>,

    /// X.509 URL Header
    ///
    /// URI that refers to a resource for an X.509 public key certificate or certificate chain.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub x5u: Option<String>,

    /// X.509 Certificate Chain
    ///
    /// Chain of one or more PKIX certificates.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub x5c: Option<Vec<String>>,

    /// X.509 Certificate SHA-1 Thumbprint
    ///
    /// base64url-encoded SHA-1 thumbprint (a.k.a. digest) of the DER encoding of an X.509 certificate.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub x5t: Option<String>,

    /// X.509 Certificate SHA-256 Thumbprint
    ///
    /// base64url-encoded SHA-256 thumbprint (a.k.a. digest) of the DER encoding of an X.509 certificate.
    #[serde(rename = "x5t#S256", alias = "x5t#s256", skip_serializing_if = "Option::is_none")]
    pub x5t_s256: Option<String>,

    // -- extra parameters -- //
    /// Additional header parameters (both public and private)
    #[serde(flatten)]
    pub additional: HashMap<String, serde_json::Value>,
}

impl JwsHeader {
    pub fn new(alg: JwsAlg) -> Self {
        Self {
            alg,
            jku: None,
            jwk: None,
            typ: None,
            cty: None,
            kid: None,
            x5u: None,
            x5c: None,
            x5t: None,
            x5t_s256: None,
            additional: HashMap::new(),
        }
    }

    pub fn new_with_cty(alg: JwsAlg, cty: impl Into<String>) -> Self {
        Self {
            cty: Some(cty.into()),
            ..Self::new(alg)
        }
    }
}

// === json web signature === //

/// Provides an API to sign any kind of data (binary). JSON claims are part of `Jwt` only.
#[derive(Debug, Clone)]
pub struct Jws {
    pub header: JwsHeader,
    pub payload: Vec<u8>,
}

impl Jws {
    pub fn new(alg: JwsAlg, payload: Vec<u8>) -> Self {
        Self {
            header: JwsHeader::new(alg),
            payload,
        }
    }

    pub fn encode(&self, private_key: &PrivateKey) -> Result<String, JwsError> {
        let header_base64 = general_purpose::URL_SAFE_NO_PAD.encode(serde_json::to_vec(&self.header)?);
        let payload_base64 = general_purpose::URL_SAFE_NO_PAD.encode(&self.payload);
        let header_and_payload = [header_base64, payload_base64].join(".");
        let signature_algo = SignatureAlgorithm::try_from(self.header.alg)?;
        let signature = signature_algo.sign(header_and_payload.as_bytes(), private_key)?;

        let signature = match self.header.alg {
            // EC sugnatures have special encoding rules (RFC 7518, section 3.4)
            JwsAlg::ES256 | JwsAlg::ES384 | JwsAlg::ES512 => {
                // Parse signature as ASN.1 DER sequence
                let signature: EcdsaSignatureValue =
                    picky_asn1_der::from_bytes(&signature).map_err(|e| SignatureError::Ec {
                        context: format!("Invalid EC DER signature encoding: {e}"),
                    })?;

                let curve = match self.header.alg {
                    JwsAlg::ES256 => EcCurve::NistP256,
                    JwsAlg::ES384 => EcCurve::NistP384,
                    JwsAlg::ES512 => EcCurve::NistP521,
                    _ => unreachable!("Checked in match above"),
                };

                let signature_component_size = curve.field_bytes_size();

                let r = signature.r.as_unsigned_bytes_be();
                let s = signature.s.as_unsigned_bytes_be();

                // We should add zero padding (leading zeros) for R & S components to match the
                // size of the curve, as ASN.1 DER encoding removes leading zeros.
                let mut jws_signature = Vec::with_capacity(signature_component_size * 2);

                let r_padding = signature_component_size - r.len();
                (0..r_padding).for_each(|_| jws_signature.push(0));
                jws_signature.extend_from_slice(r);

                let s_padding = signature_component_size - s.len();
                (0..s_padding).for_each(|_| jws_signature.push(0));
                jws_signature.extend_from_slice(s);

                jws_signature
            }
            _ => signature,
        };

        let signature_base64 = general_purpose::URL_SAFE_NO_PAD.encode(signature);
        Ok([header_and_payload, signature_base64].join("."))
    }

    /// Verifies signature and returns decoded JWS payload.
    pub fn decode(encoded_token: &str, public_key: &PublicKey) -> Result<Self, JwsError> {
        RawJws::decode(encoded_token).and_then(|raw_jws| raw_jws.verify(public_key))
    }
}

/// Raw low-level interface to the yet to be verified JWS token.
///
/// This is useful to inspect the structure before performing further processing.
/// For most usecases, use `Jws` directly.
#[derive(Debug, Clone)]
pub struct RawJws<'repr> {
    pub compact_repr: Cow<'repr, str>,
    pub header: JwsHeader,
    payload: Vec<u8>,
    pub signature: Vec<u8>,
}

/// An owned `RawJws` for convenience.
pub type OwnedRawJws = RawJws<'static>;

impl<'repr> RawJws<'repr> {
    /// Decodes a JWS in compact representation.
    pub fn decode(compact_repr: impl Into<Cow<'repr, str>>) -> Result<Self, JwsError> {
        decode_impl(compact_repr.into())
    }

    /// Peeks the payload before signature verification.
    pub fn peek_payload(&self) -> &[u8] {
        &self.payload
    }

    /// Verifies signature and returns a verified `Jws` structure.
    pub fn verify(self, public_key: &PublicKey) -> Result<Jws, JwsError> {
        verify_signature(&self.compact_repr, public_key, self.header.alg)?;
        Ok(self.discard_signature())
    }

    /// Discards the signature without verifying it and hands a `Jws` structure.
    ///
    /// Generally, you should not do that.
    pub fn discard_signature(self) -> Jws {
        Jws {
            header: self.header,
            payload: self.payload,
        }
    }
}

fn decode_impl(compact_repr: Cow<'_, str>) -> Result<RawJws<'_>, JwsError> {
    let first_dot_idx = compact_repr.find('.').ok_or_else(|| JwsError::InvalidEncoding {
        input: compact_repr.clone().into_owned(),
    })?;

    let last_dot_idx = compact_repr.rfind('.').ok_or_else(|| JwsError::InvalidEncoding {
        input: compact_repr.clone().into_owned(),
    })?;

    if first_dot_idx == last_dot_idx || compact_repr.starts_with('.') || compact_repr.ends_with('.') {
        return Err(JwsError::InvalidEncoding {
            input: compact_repr.into_owned(),
        });
    }

    let header_json = general_purpose::URL_SAFE_NO_PAD.decode(&compact_repr[..first_dot_idx])?;
    let header = serde_json::from_slice::<JwsHeader>(&header_json)?;

    let signature = general_purpose::URL_SAFE_NO_PAD.decode(&compact_repr[last_dot_idx + 1..])?;

    let payload = general_purpose::URL_SAFE_NO_PAD.decode(&compact_repr[first_dot_idx + 1..last_dot_idx])?;

    Ok(RawJws {
        compact_repr,
        header,
        payload,
        signature,
    })
}

/// JWS verification primitive
pub fn verify_signature(encoded_token: &str, public_key: &PublicKey, algorithm: JwsAlg) -> Result<(), JwsError> {
    let last_dot_idx = encoded_token.rfind('.').ok_or_else(|| JwsError::InvalidEncoding {
        input: encoded_token.to_owned(),
    })?;

    if encoded_token.ends_with('.') {
        return Err(JwsError::InvalidEncoding {
            input: encoded_token.to_owned(),
        });
    }

    let signature = general_purpose::URL_SAFE_NO_PAD.decode(&encoded_token[last_dot_idx + 1..])?;
    let signature_algo = SignatureAlgorithm::try_from(algorithm)?;

    let signature = match algorithm {
        // Special decoding rules for ECDSA
        JwsAlg::ES256 | JwsAlg::ES384 | JwsAlg::ES512 => {
            let curve = match algorithm {
                JwsAlg::ES256 => EcCurve::NistP256,
                JwsAlg::ES384 => EcCurve::NistP384,
                JwsAlg::ES512 => EcCurve::NistP521,
                _ => unreachable!("Checked in match above"),
            };

            let component_size = curve.field_bytes_size();
            let jws_encoded_signature_size = component_size * 2;

            if signature.len() != jws_encoded_signature_size {
                return Err(SignatureError::Ec {
                    context: format!(
                        "Invalid JWS EC signature size. Expected: {}; Actual: {}",
                        jws_encoded_signature_size,
                        signature.len()
                    ),
                }
                .into());
            }

            let (r, s) = signature.split_at(component_size);

            let signature = EcdsaSignatureValue {
                r: IntegerAsn1::from_bytes_be_unsigned(r.to_vec()),
                s: IntegerAsn1::from_bytes_be_unsigned(s.to_vec()),
            };

            picky_asn1_der::to_vec(&signature).map_err(|e| SignatureError::Ec {
                context: format!("Failed to encode EC signature to DER format: {e}"),
            })?
        }
        _ => signature,
    };

    signature_algo.verify(public_key, &encoded_token.as_bytes()[..last_dot_idx], &signature)?;

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::pem::Pem;
    use rstest::rstest;

    const PAYLOAD: &str = r#"{"sub":"1234567890","name":"John Doe","admin":true,"iat":1516239022}"#;

    fn get_private_key_1() -> PrivateKey {
        let pk_pem = picky_test_data::RSA_2048_PK_1.parse::<Pem>().unwrap();
        PrivateKey::from_pem(&pk_pem).unwrap()
    }

    fn get_private_key_2() -> PrivateKey {
        let pk_pem = picky_test_data::RSA_2048_PK_7.parse::<Pem>().unwrap();
        PrivateKey::from_pem(&pk_pem).unwrap()
    }

    #[test]
    fn encode_rsa_sha256() {
        let jwt = Jws {
            header: JwsHeader {
                typ: Some(String::from("JWT")),
                ..JwsHeader::new(JwsAlg::RS256)
            },
            payload: PAYLOAD.as_bytes().to_vec(),
        };
        let encoded = jwt.encode(&get_private_key_1()).unwrap();
        assert_eq!(encoded, picky_test_data::JOSE_JWT_SIG_EXAMPLE);
    }

    #[rstest]
    #[case(JwsAlg::ES256, picky_test_data::EC_NIST256_PK_1)]
    #[case(JwsAlg::ES384, picky_test_data::EC_NIST384_PK_1)]
    #[case(JwsAlg::ES512, picky_test_data::EC_NIST521_PK_1)]
    fn ecdsa_sign_verify(#[case] alg: JwsAlg, #[case] key_pem: &str) {
        let key = PrivateKey::from_pem_str(key_pem).unwrap();

        let jwt = Jws {
            header: JwsHeader {
                typ: Some(String::from("JWT")),
                ..JwsHeader::new(alg)
            },
            payload: PAYLOAD.as_bytes().to_vec(),
        };

        // Check encode + sign
        let encoded = jwt.encode(&key).unwrap();

        // Check decode + verify
        let jws = RawJws::decode(&encoded).unwrap();
        jws.clone().verify(&key.to_public_key().unwrap()).unwrap();

        assert_eq!(&jws.header, &jwt.header);
        assert_eq!(&jws.payload, &jwt.payload);
    }

    #[rstest]
    #[case(picky_test_data::EC_NIST256_PK_1, picky_test_data::JOSE_JWT_SIG_ES256)]
    #[case(picky_test_data::EC_NIST384_PK_1, picky_test_data::JOSE_JWT_SIG_ES384)]
    #[case(picky_test_data::EC_NIST521_PK_1, picky_test_data::JOSE_JWT_SIG_ES512)]
    fn ecdsa_parse_and_verify(#[case] key_pem: &str, #[case] signature: &str) {
        let key = PrivateKey::from_pem_str(key_pem).unwrap();

        // Check decode + verify
        let jws = RawJws::decode(signature).unwrap();
        jws.clone().verify(&key.to_public_key().unwrap()).unwrap();
    }

    const JWT_ED25519_BODY: &str = r#"{"username":"kataras"}"#;
    const JWT_ED25519_GO_BODY: &str = r#"{"foo":"bar"}"#;

    /// Validate that invalid header with alg set to "ED25519" could be parsed by picky
    #[rstest]
    #[allow(deprecated)]
    #[case(
        picky_test_data::JOSE_JWT_SIG_ED25519_GO_PRIVATE_KEY,
        picky_test_data::JOSE_JWT_SIG_ED25519_GO,
        JwsAlg::ED25519,
        JWT_ED25519_GO_BODY
    )]
    #[case(
        picky_test_data::JOSE_JWT_SIG_ED25519_PRIVATE_KEY,
        picky_test_data::JOSE_JWT_SIG_ED25519,
        JwsAlg::EdDSA,
        JWT_ED25519_BODY
    )]
    fn ed25519_algorithm(#[case] key: &str, #[case] encoded_expected: &str, #[case] alg: JwsAlg, #[case] body: &str) {
        let key = PrivateKey::from_pem_str(key).unwrap();

        let jwt = Jws {
            header: JwsHeader {
                typ: Some(String::from("JWT")),
                ..JwsHeader::new(alg)
            },
            payload: body.as_bytes().to_vec(),
        };

        // Check encode + sign
        let encoded = jwt.encode(&key).unwrap();
        assert_eq!(encoded, encoded_expected);

        // Check decode + verify
        let jws = RawJws::decode(&encoded).unwrap();
        jws.verify(&key.to_public_key().unwrap()).unwrap();
    }

    #[test]
    fn decode_rsa_sha256() {
        let public_key = get_private_key_1().to_public_key().unwrap();
        let jwt = Jws::decode(picky_test_data::JOSE_JWT_SIG_EXAMPLE, &public_key).unwrap();
        assert_eq!(jwt.payload.as_slice(), PAYLOAD.as_bytes());
    }

    #[test]
    fn decode_rsa_sha256_delayed_signature_check() {
        let jws = RawJws::decode(picky_test_data::JOSE_JWT_SIG_EXAMPLE).unwrap();
        println!("{}", String::from_utf8_lossy(&jws.payload));
        assert_eq!(jws.peek_payload(), PAYLOAD.as_bytes());

        let public_key = get_private_key_2().to_public_key().unwrap();
        let err = jws.verify(&public_key).err().unwrap();
        assert_eq!(err.to_string(), "signature error: invalid signature");
    }

    #[test]
    fn decode_rsa_sha256_invalid_signature_err() {
        let public_key = get_private_key_2().to_public_key().unwrap();
        let err = Jws::decode(picky_test_data::JOSE_JWT_SIG_EXAMPLE, &public_key)
            .err()
            .unwrap();
        assert_eq!(err.to_string(), "signature error: invalid signature");
    }

    #[test]
    fn decode_invalid_base64_err() {
        let public_key = get_private_key_1().to_public_key().unwrap();
        let err = Jws::decode("aieoè~†.tésp.à", &public_key).err().unwrap();
        assert_eq!(
            err.to_string(),
            "couldn\'t decode base64: Invalid symbol 195, offset 4."
        );
    }

    #[test]
    fn decode_invalid_json_err() {
        let public_key = get_private_key_1().to_public_key().unwrap();

        let err = Jws::decode("abc.abc.abc", &public_key).err().unwrap();
        assert_eq!(err.to_string(), "JSON error: expected value at line 1 column 1");

        let err = Jws::decode("eyAiYWxnIjogIkhTMjU2IH0K.abc.abc", &public_key)
            .err()
            .unwrap();
        assert_eq!(
            err.to_string(),
            "JSON error: control character (\\u0000-\\u001F) \
             found while parsing a string at line 2 column 0"
        );
    }

    #[test]
    fn decode_invalid_encoding_err() {
        let public_key = get_private_key_1().to_public_key().unwrap();

        let err = Jws::decode(".abc.abc", &public_key).err().unwrap();
        assert_eq!(err.to_string(), "input isn\'t a valid token string: .abc.abc");

        let err = Jws::decode("abc.abc.", &public_key).err().unwrap();
        assert_eq!(err.to_string(), "input isn\'t a valid token string: abc.abc.");

        let err = Jws::decode("abc.abc", &public_key).err().unwrap();
        assert_eq!(err.to_string(), "input isn\'t a valid token string: abc.abc");

        let err = Jws::decode("abc", &public_key).err().unwrap();
        assert_eq!(err.to_string(), "input isn\'t a valid token string: abc");
    }
}
