Comments (4)
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.
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.
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.
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)
- Only decode header HOT 2
- Decoding/Verifying with JWK Set or Verifier Selector HOT 8
- set_x509_certificate_chain (x5c) implementation is wrong HOT 3
- JWS with x5c header parameter HOT 7
- x509_certificate_chain must be encoded in STANDARD, not URL_SAFE_NO_PAD or STANDARD_NOPAD HOT 1
- Jws/JweHeaderSet critical value should not be encoded by BASE64 HOT 1
- RUSTSEC-2023-0022 to 0024 => security advisory on openssl HOT 1
- Support the `ECMR` operation for JWK exchange HOT 2
- Allow to enable openssl vendored feature to cross compile HOT 4
- How to get verifier from algorithm name or JWK HOT 4
- An equivalent function to node-jose's createEncrypt
- Security vulnerability disclosure HOT 2
- Support for Brainpool curves HOT 2
- Wrong iv length in JWE header HOT 3
- [QUESTION] How can I cast JoseHeader to JwsHeader?
- Allow deserialization of JwtPayload HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from josekit-rs.