Git Product home page Git Product logo

Comments (4)

wellcaffeinated avatar wellcaffeinated commented on June 23, 2024

I was stuck on this for hours until I saw this code.

I think likely the library should have the selectors just return Box<dyn JwsVerifier>... but they don't for some reason.

What I did was:

fn get_jws_verifier(jwk: &Jwk, header: &JwsHeader) -> Result<Option<Box<dyn JwsVerifier>>, JoseError> {
  match header.algorithm() {
    Some("RS256") => Ok(Some(Box::new(RS256.verifier_from_jwk(jwk)?))),
    Some("RS384") => Ok(Some(Box::new(RS384.verifier_from_jwk(jwk)?))),
    Some("RS512") => Ok(Some(Box::new(RS512.verifier_from_jwk(jwk)?))),
    Some("ES256") => Ok(Some(Box::new(ES256.verifier_from_jwk(jwk)?))),
    Some("ES256K") => Ok(Some(Box::new(ES256K.verifier_from_jwk(jwk)?))),
    Some("ES384") => Ok(Some(Box::new(ES384.verifier_from_jwk(jwk)?))),
    Some("ES512") => Ok(Some(Box::new(ES512.verifier_from_jwk(jwk)?))),
    Some("EdDSA") => Ok(Some(Box::new(EdDSA.verifier_from_jwk(jwk)?))),
    _ => Ok(None),
  }
}

let selector = |header: &JwsHeader| -> Result<Option<&dyn JwsVerifier>, JoseError> {
  get_jws_verifier(jwk, header).map(|v| v.map(|v| {
    let leaked: &dyn JwsVerifier = Box::leak(v);
    leaked
  }))
};

from josekit-rs.

alexisgaziello avatar alexisgaziello commented on June 23, 2024

feels like the function get_jws_verifier should be part of the library right?

Also, do you understand why we have to use Box::leak? isn't that leaking memory?

I am running this on every authenticated request so if I leak every request that doesn't sound good. Probably my understanding of this is wrong

from josekit-rs.

wellcaffeinated avatar wellcaffeinated commented on June 23, 2024

Ok I think i figured out a better option:

use anyhow::anyhow;
use josekit::{jwk::Jwk, jws::{JwsSigner, *}, JoseError};

use crate::signer::SigningError;

fn get_jws_signer(jwk: &Jwk, header: &JwsHeader) -> Result<Box<dyn JwsSigner>, JoseError> {
  match header.algorithm() {
    Some("RS256") => Ok(Box::new(RS256.signer_from_jwk(jwk)?)),
    Some("RS384") => Ok(Box::new(RS384.signer_from_jwk(jwk)?)),
    Some("RS512") => Ok(Box::new(RS512.signer_from_jwk(jwk)?)),
    Some("ES256") => Ok(Box::new(ES256.signer_from_jwk(jwk)?)),
    Some("ES256K") => Ok(Box::new(ES256K.signer_from_jwk(jwk)?)),
    Some("ES384") => Ok(Box::new(ES384.signer_from_jwk(jwk)?)),
    Some("ES512") => Ok(Box::new(ES512.signer_from_jwk(jwk)?)),
    Some("EdDSA") => Ok(Box::new(EdDSA.signer_from_jwk(jwk)?)),
    _ => return Err(JoseError::UnsupportedSignatureAlgorithm(anyhow!("Unsupported algorithm {}", header.algorithm().unwrap_or("none")))),
  }
}

fn get_header(jwk: &Jwk) -> JwsHeader {
  let alg = match jwk.key_type() {
    "RSA" => {
      match jwk.curve() {
        Some("P-256") => "RS256",
        Some("P-384") => "RS384",
        Some("P-521") => "RS512",
        _ => "RS256",
      }
    },
    "EC" => {
      match jwk.curve() {
        Some("P-256") => "ES256",
        Some("P-384") => "ES384",
        Some("P-521") => "ES512",
        _ => "ES256",
      }
    },
    "OKP" => {
      match jwk.curve() {
        Some("Ed25519") => "EdDSA",
        _ => "EdDSA",
      }
    },
    _ => panic!("Unsupported key type")
  };
  let mut h = JwsHeader::new();
  h.set_algorithm(alg);
  h
}

struct SignerSelector<'a> {
  jwk: &'a Jwk,
  signer: Option<Box<dyn JwsSigner>>,
}

impl<'a> SignerSelector<'a> {
  pub fn new(jwk: &'a Jwk) -> Self {
    Self {
      jwk,
      signer: None,
    }
  }

  pub fn select(&mut self, header: &JwsHeader) -> Result<&dyn JwsSigner, JoseError> {
    self.signer = Some(get_jws_signer(self.jwk, header)?);
    Ok(self.signer.as_deref().map(|v| v).unwrap())
  }
}

pub fn sign<P: AsRef<[u8]>>(jwk: &Jwk, payload: P) -> Result<String, SigningError> {
  let mut selector = SignerSelector::new(jwk);
  let header = get_header(jwk);
  let signer = selector.select(&header)
    .map_err(|e| SigningError(e.to_string()))?;
  serialize_compact(payload.as_ref(), &header, signer)
    .map_err(|e| SigningError(format!("Could not sign: {}", e)))
}

from josekit-rs.

wellcaffeinated avatar wellcaffeinated commented on June 23, 2024

Here's what I'm using for verification:

use base64::Engine;
use josekit::{jwk::Jwk, jws::{JwsVerifier, *}, JoseError};
use crate::errors::VerificationError;

fn get_jws_verifier(jwk: &Jwk, header: &JwsHeader) -> Result<Option<Box<dyn JwsVerifier>>, JoseError> {
  match header.algorithm() {
    Some("RS256") => Ok(Some(Box::new(RS256.verifier_from_jwk(jwk)?))),
    Some("RS384") => Ok(Some(Box::new(RS384.verifier_from_jwk(jwk)?))),
    Some("RS512") => Ok(Some(Box::new(RS512.verifier_from_jwk(jwk)?))),
    Some("ES256") => Ok(Some(Box::new(ES256.verifier_from_jwk(jwk)?))),
    Some("ES256K") => Ok(Some(Box::new(ES256K.verifier_from_jwk(jwk)?))),
    Some("ES384") => Ok(Some(Box::new(ES384.verifier_from_jwk(jwk)?))),
    Some("ES512") => Ok(Some(Box::new(ES512.verifier_from_jwk(jwk)?))),
    Some("EdDSA") => Ok(Some(Box::new(EdDSA.verifier_from_jwk(jwk)?))),
    _ => Ok(None),
  }
}

struct VerifierSelector<'a> {
  jwk: &'a Jwk,
  verifier: Option<Box<dyn JwsVerifier>>,
}

impl<'a> VerifierSelector<'a> {
  pub fn new(jwk: &'a Jwk) -> Self {
    Self {
      jwk,
      verifier: None,
    }
  }
}

impl<'a> VerifierSelector<'a> {
  pub fn select(&mut self, header: &JwsHeader) -> Result<&dyn JwsVerifier, JoseError> {
    self.verifier = get_jws_verifier(self.jwk, header)?;
    Ok(self.verifier.as_deref().map(|v| v).unwrap())
  }
}

fn get_header(input: impl AsRef<[u8]>) -> Result<JwsHeader, JoseError> {
  let input = input.as_ref();
  let indexies: Vec<usize> = input
      .iter()
      .enumerate()
      .filter(|(_, b)| **b == b'.' as u8)
      .map(|(pos, _)| pos)
      .collect();
  if indexies.len() != 2 {
    JoseError::InvalidJwsFormat(
      anyhow::anyhow!("The compact serialization form of JWS must be three parts separated by colon.")
    );
  }

  let header = &input[0..indexies[0]];
  // let payload = &input[(indexies[0] + 1)..(indexies[1])];
  // let signature = &input[(indexies[1] + 1)..];

  use josekit::{Map, Value};
  let header = base64::engine::general_purpose::URL_SAFE_NO_PAD.decode(header)
    .map_err(|e| JoseError::InvalidJwsFormat(anyhow::anyhow!(e.to_string())))?;
  let header: Map<String, Value> = serde_json::from_slice(&header)
    .map_err(|e| JoseError::InvalidJwsFormat(anyhow::anyhow!(e.to_string())))?;
  let header = JwsHeader::from_map(header)?;
  Ok(header)
}

pub fn verify_signature<S: AsRef<str>, P: AsRef<[u8]>>(jwk: &Jwk, signature: S, expected_payload: P) -> Result<(), VerificationError> {
  let mut selector = VerifierSelector::new(jwk);
  let header = get_header(signature.as_ref())
    .map_err(|e| VerificationError::InvalidFormat(e.to_string()))?;
  let verifier = selector.select(&header)
    .map_err(|e| VerificationError::InvalidFormat(e.to_string()))?;
  // this checks sig
  let (payload, _) = deserialize_compact(signature.as_ref(), verifier).map_err(|e| {
    match e {
      JoseError::InvalidSignature(e) => VerificationError::BadSignature(e.to_string()),
      _ => VerificationError::InvalidFormat("Signature was not formatted as compact JWS".to_string()),
    }
  })?;
  // check the content hash
  if expected_payload.as_ref() != payload {
    return Err(VerificationError::BadSignature("Payload does not match signature".to_string()));
  }
  Ok(())
}

from josekit-rs.

Related Issues (17)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.