Browse Source

Added very simple Ed25519 JWT signing/verification.

Kestrel 1 năm trước cách đây
mục cha
commit
e87fe04ae5
3 tập tin đã thay đổi với 94 bổ sung3 xóa
  1. 82 0
      src/jwt.rs
  2. 7 1
      src/server/config.rs
  3. 5 2
      src/server/oidc.rs

+ 82 - 0
src/jwt.rs

@@ -15,3 +15,85 @@ impl<'l> Into<String> for JWTData<'l> {
         serde_json::to_string(&self).unwrap()
         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());
+    }
+}

+ 7 - 1
src/server/config.rs

@@ -2,10 +2,16 @@ use crate::schema;
 use microrm::prelude::*;
 use microrm::prelude::*;
 use serde::{Deserialize, Serialize};
 use serde::{Deserialize, Serialize};
 
 
+fn default_auth_token_expiry() -> u64 {
+    600
+}
+
 #[derive(Serialize, Deserialize)]
 #[derive(Serialize, Deserialize)]
 pub struct ServerConfig {
 pub struct ServerConfig {
     pub base_url: String,
     pub base_url: String,
-    // #[serde(defaul
+
+    #[serde(default = "default_auth_token_expiry")]
+    pub auth_token_expiry: u64,
 }
 }
 
 
 impl ServerConfig {
 impl ServerConfig {

+ 5 - 2
src/server/oidc.rs

@@ -99,12 +99,15 @@ fn do_token_authorize(
         request.param("realm").unwrap()
         request.param("realm").unwrap()
     );
     );
 
 
+    let iat = std::time::SystemTime::now();
+    let exp = iat + std::time::Duration::from_secs(request.state().core.config.auth_token_expiry);
+
     let token = jwt::JWTData {
     let token = jwt::JWTData {
         sub: user.username.as_str(),
         sub: user.username.as_str(),
         iss: issuer.as_str(),
         iss: issuer.as_str(),
         aud: client.shortname.as_str(),
         aud: client.shortname.as_str(),
-        iat: 123,
-        exp: 123,
+        iat: iat.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(),
+        exp: exp.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(),
         extras: Default::default(),
         extras: Default::default(),
     };
     };