123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- #[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<String> 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<B: std::convert::AsRef<[u8]>>(with: &ring::signature::UnparsedPublicKey<B>, from: &str) -> Option<Self> {
- 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(<JWTData as Into::<String>>::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());
- }
- }
|