jwt.rs 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. #[derive(serde::Serialize, serde::Deserialize)]
  2. pub struct JWTData<'l> {
  3. pub sub: &'l str,
  4. pub iss: &'l str,
  5. pub aud: &'l str,
  6. pub iat: u64,
  7. pub exp: u64,
  8. #[serde(flatten)]
  9. pub extras: std::collections::HashMap<&'l str, serde_json::Value>,
  10. }
  11. impl<'l> Into<String> for JWTData<'l> {
  12. fn into(self) -> String {
  13. serde_json::to_string(&self).unwrap()
  14. }
  15. }
  16. pub const DEFAULT_HEADER: &'static str = r#"{"alg": "EdDSA", "typ": "JWT"}"#;
  17. pub struct JWT {
  18. pub header: String,
  19. pub data: String,
  20. pub signature: String,
  21. }
  22. impl JWT {
  23. pub fn verify<B: std::convert::AsRef<[u8]>>(with: &ring::signature::UnparsedPublicKey<B>, from: &str) -> Option<Self> {
  24. let header_split = from.find(".")?;
  25. let header = &from[0..header_split];
  26. let data_split = header_split + 1 + from[header_split+1..].find(".")?;
  27. let data = &from[header_split + 1 .. data_split];
  28. let signature = &from[data_split + 1 ..];
  29. let mut to_verify = vec![];
  30. to_verify.extend(header.as_bytes());
  31. to_verify.extend(".".as_bytes());
  32. to_verify.extend(data.as_bytes());
  33. let decoded_signature = base64::decode_config(signature.as_bytes(), base64::URL_SAFE_NO_PAD).ok()?;
  34. with.verify(to_verify.as_ref(), decoded_signature.as_ref()).ok()?;
  35. // if we got this far, the verification passed
  36. Some(Self {
  37. header: header.into(), data: data.into(), signature: signature.into()
  38. })
  39. }
  40. pub fn sign(with: &ring::signature::Ed25519KeyPair, data: JWTData) -> Self {
  41. let header = base64::encode_config(DEFAULT_HEADER, base64::URL_SAFE_NO_PAD);
  42. let data = base64::encode_config(<JWTData as Into::<String>>::into(data), base64::URL_SAFE_NO_PAD);
  43. let mut to_sign = vec![];
  44. to_sign.extend(header.as_bytes());
  45. to_sign.extend(".".as_bytes());
  46. to_sign.extend(data.as_bytes());
  47. let signature = base64::encode_config(with.sign(&to_sign).as_ref(), base64::URL_SAFE_NO_PAD);
  48. Self {
  49. header,
  50. data,
  51. signature
  52. }
  53. }
  54. pub fn into_string(self) -> String {
  55. self.header + "." + self.data.as_str() + "." + self.signature.as_str()
  56. }
  57. }
  58. #[cfg(test)]
  59. mod test {
  60. use ring::signature::KeyPair;
  61. #[test]
  62. fn simple_round_trip() {
  63. let rng = ring::rand::SystemRandom::new();
  64. let kpair_raw = ring::signature::Ed25519KeyPair::generate_pkcs8(&rng).expect("couldn't generate ephemeral keypair");
  65. let kpair = ring::signature::Ed25519KeyPair::from_pkcs8(kpair_raw.as_ref()).expect("couldn't load keypair");
  66. let jdata = super::JWTData {
  67. sub: "sub",
  68. iss: "iss",
  69. aud: "aud",
  70. iat: 1,
  71. exp: 2,
  72. extras: Default::default(),
  73. };
  74. let generated = super::JWT::sign(&kpair, jdata).into_string();
  75. println!("generated: {:?}", generated);
  76. let pubkey = ring::signature::UnparsedPublicKey::new(&ring::signature::ED25519, kpair.public_key().as_ref());
  77. let vresult = super::JWT::verify(&pubkey, generated.as_str());
  78. assert!(vresult.is_some());
  79. }
  80. }