-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[QUESTION] how to properly use decode_with_verifier_in_jwk_set #34
Comments
I was stuck on this for hours until I saw this code. I think likely the library should have the selectors just return 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
}))
}; |
feels like the function 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 |
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)))
} |
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(())
} |
Hey,
I am new to rust and I am having trouble using the library. Could you help me?
I have a jwk_set, and I want to use it to decode a token. I am unsure what to put in the arg
selector
.I am trying to use
decode_with_verifier_in_jwk_set
but having little success. ChatGpt suggested something like:But seems weird and overly complex. I would appreciate any help.
Thank you
The text was updated successfully, but these errors were encountered: