#[derive(serde::Serialize, serde::Deserialize)] pub struct JWTData<'l> { pub sub: &'l str, pub iss: &'l str, pub aud: &'l str, pub iat: u64, pub exp: u64, #[serde(flatten)] pub extras: std::collections::HashMap<&'l str, serde_json::Value>, } impl<'l> Into for JWTData<'l> { fn into(self) -> String { serde_json::to_string(&self).unwrap() } } pub const DEFAULT_HEADER: &'static str = r#"{"alg": "EdDSA", "typ": "JWT"}"#; pub struct JWT { pub header: String, pub data: String, pub signature: String, } impl JWT { pub fn verify>(with: &ring::signature::UnparsedPublicKey, from: &str) -> Option { let header_split = from.find(".")?; let header = &from[0..header_split]; let data_split = header_split + 1 + from[header_split+1..].find(".")?; let data = &from[header_split + 1 .. data_split]; let signature = &from[data_split + 1 ..]; let mut to_verify = vec![]; to_verify.extend(header.as_bytes()); to_verify.extend(".".as_bytes()); to_verify.extend(data.as_bytes()); let decoded_signature = base64::decode_config(signature.as_bytes(), base64::URL_SAFE_NO_PAD).ok()?; with.verify(to_verify.as_ref(), decoded_signature.as_ref()).ok()?; // if we got this far, the verification passed Some(Self { header: header.into(), data: data.into(), signature: signature.into() }) } pub fn sign(with: &ring::signature::Ed25519KeyPair, data: JWTData) -> Self { let header = base64::encode_config(DEFAULT_HEADER, base64::URL_SAFE_NO_PAD); let data = base64::encode_config(>::into(data), base64::URL_SAFE_NO_PAD); let mut to_sign = vec![]; to_sign.extend(header.as_bytes()); to_sign.extend(".".as_bytes()); to_sign.extend(data.as_bytes()); let signature = base64::encode_config(with.sign(&to_sign).as_ref(), base64::URL_SAFE_NO_PAD); Self { header, data, signature } } pub fn into_string(self) -> String { self.header + "." + self.data.as_str() + "." + self.signature.as_str() } } #[cfg(test)] mod test { use ring::signature::KeyPair; #[test] fn simple_round_trip() { let rng = ring::rand::SystemRandom::new(); let kpair_raw = ring::signature::Ed25519KeyPair::generate_pkcs8(&rng).expect("couldn't generate ephemeral keypair"); let kpair = ring::signature::Ed25519KeyPair::from_pkcs8(kpair_raw.as_ref()).expect("couldn't load keypair"); let jdata = super::JWTData { sub: "sub", iss: "iss", aud: "aud", iat: 1, exp: 2, extras: Default::default(), }; let generated = super::JWT::sign(&kpair, jdata).into_string(); println!("generated: {:?}", generated); let pubkey = ring::signature::UnparsedPublicKey::new(&ring::signature::ED25519, kpair.public_key().as_ref()); let vresult = super::JWT::verify(&pubkey, generated.as_str()); assert!(vresult.is_some()); } }