-
Notifications
You must be signed in to change notification settings - Fork 90
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
457 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
[package] | ||
name = "identity_ecdsa_verifier" | ||
# TODO: description for consistency? | ||
version = "0.1.0" | ||
authors.workspace = true | ||
edition.workspace = true | ||
homepage.workspace = true | ||
license.workspace = true | ||
rust-version.workspace = true | ||
|
||
[lints] | ||
workspace = true | ||
|
||
[dependencies] | ||
identity_verification.workspace = true | ||
k256 = { workspace = true, features = ["std", "ecdsa", "ecdsa-core"], optional = true } | ||
p256 = { workspace = true, features = ["std", "ecdsa", "ecdsa-core"], optional = true } | ||
signature.workspace = true | ||
|
||
[dev-dependencies] | ||
josekit.workspace = true | ||
serde_json.workspace = true | ||
|
||
[features] | ||
default = ["es256", "es256k"] | ||
# Enables the EcDSAJwsVerifier to verify JWS with alg = ES256. | ||
es256 = ["dep:p256"] | ||
# Enables the EcDSAJwsVerifier to verify JWS with alg = ES256K. | ||
es256k = ["dep:k256"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# ECDSA Verifier | ||
|
||
This crate implements a `JwsVerifier` capable of verifying EcDSA signatures with algorithms `ES256` and `ES256K`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
use identity_verification::jws::JwsAlgorithm; | ||
use identity_verification::jws::JwsVerifier; | ||
use identity_verification::jws::SignatureVerificationErrorKind; | ||
|
||
/// An implementor of [`JwsVerifier`](identity_verification::jws::JwsVerifier) | ||
/// that can handle a selection of EcDSA algorithms. | ||
/// | ||
/// The following algorithms are supported, if the respective feature on the | ||
/// crate is activated: | ||
/// | ||
/// - [`JwsAlgorithm::ES256`](identity_verification::jws::JwsAlgorithm::ES256). | ||
/// - [`JwsAlgorithm::ES256K`](identity_verification::jws::JwsAlgorithm::ES256K). | ||
#[derive(Debug, Default)] | ||
#[non_exhaustive] | ||
pub struct EcDSAJwsVerifier {} | ||
|
||
impl JwsVerifier for EcDSAJwsVerifier { | ||
fn verify( | ||
&self, | ||
input: identity_verification::jws::VerificationInput, | ||
public_key: &identity_verification::jwk::Jwk, | ||
) -> Result<(), identity_verification::jws::SignatureVerificationError> { | ||
match input.alg { | ||
#[cfg(feature = "es256")] | ||
JwsAlgorithm::ES256 => crate::Secp256R1Verifier::verify(&input, public_key), | ||
#[cfg(feature = "es256k")] | ||
JwsAlgorithm::ES256K => crate::Secp256K1Verifier::verify(&input, public_key), | ||
_ => Err(SignatureVerificationErrorKind::UnsupportedAlg.into()), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#![doc = include_str!("./../README.md")] | ||
#![warn( | ||
rust_2018_idioms, | ||
unreachable_pub, | ||
missing_docs, | ||
rustdoc::missing_crate_level_docs, | ||
rustdoc::broken_intra_doc_links, | ||
rustdoc::private_intra_doc_links, | ||
rustdoc::private_doc_tests, | ||
clippy::missing_safety_doc | ||
)] | ||
|
||
mod ecdsa_jws_verifier; | ||
#[cfg(feature = "es256k")] | ||
mod secp256k1; | ||
#[cfg(feature = "es256")] | ||
mod secp256r1; | ||
|
||
pub use ecdsa_jws_verifier::*; | ||
#[cfg(feature = "es256k")] | ||
pub use secp256k1::*; | ||
#[cfg(feature = "es256")] | ||
pub use secp256r1::*; | ||
|
||
#[cfg(test)] | ||
mod tests; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
use std::ops::Deref; | ||
|
||
use identity_verification::jwk::JwkParamsEc; | ||
use identity_verification::jws::SignatureVerificationError; | ||
use identity_verification::jws::SignatureVerificationErrorKind; | ||
use identity_verification::jwu::{self}; | ||
use k256::ecdsa::Signature; | ||
use k256::ecdsa::VerifyingKey; | ||
use k256::elliptic_curve::sec1::FromEncodedPoint; | ||
use k256::elliptic_curve::subtle::CtOption; | ||
use k256::EncodedPoint; | ||
use k256::PublicKey; | ||
|
||
/// A verifier that can handle the | ||
/// [`JwsAlgorithm::ES256K`](identity_verification::jws::JwsAlgorithm::ES256K) | ||
/// algorithm. | ||
#[derive(Debug, Default)] | ||
#[non_exhaustive] | ||
pub struct Secp256K1Verifier {} | ||
|
||
impl Secp256K1Verifier { | ||
/// Verify a JWS signature secured with the | ||
/// [`JwsAlgorithm::ES256K`](identity_verification::jws::JwsAlgorithm::ES256K) | ||
/// algorithm. | ||
/// | ||
/// This function is useful when one is building a | ||
/// [`JwsVerifier`](identity_verification::jws::JwsVerifier) that | ||
/// handles the | ||
/// [`JwsAlgorithm::ES256K`](identity_verification::jws::JwsAlgorithm::ES256K) | ||
/// in the same manner as the [`Secp256K1Verifier`] hence extending its | ||
/// capabilities. | ||
/// | ||
/// # Warning | ||
/// | ||
/// This function does not check whether `alg = ES256K` in the protected | ||
/// header. Callers are expected to assert this prior to calling the | ||
/// function. | ||
pub fn verify( | ||
input: &identity_verification::jws::VerificationInput, | ||
public_key: &identity_verification::jwk::Jwk, | ||
) -> Result<(), SignatureVerificationError> { | ||
// Obtain a K256 public key. | ||
let params: &JwkParamsEc = public_key | ||
.try_ec_params() | ||
.map_err(|_| SignatureVerificationErrorKind::UnsupportedKeyType)?; | ||
|
||
// Concatenate x and y coordinates as required by | ||
// EncodedPoint::from_untagged_bytes. | ||
let public_key_bytes = jwu::decode_b64(¶ms.x) | ||
.map_err(|err| { | ||
SignatureVerificationError::new(SignatureVerificationErrorKind::KeyDecodingFailure) | ||
.with_source(err) | ||
})? | ||
.into_iter() | ||
.chain(jwu::decode_b64(¶ms.y).map_err(|err| { | ||
SignatureVerificationError::new(SignatureVerificationErrorKind::KeyDecodingFailure) | ||
.with_source(err) | ||
})?) | ||
.collect(); | ||
|
||
// The JWK contains the uncompressed x and y coordinates, so we can create the | ||
// encoded point directly without prefixing an SEC1 tag. | ||
let encoded_point: EncodedPoint = EncodedPoint::from_untagged_bytes(&public_key_bytes); | ||
let public_key: PublicKey = { | ||
let opt_public_key: CtOption<PublicKey> = PublicKey::from_encoded_point(&encoded_point); | ||
if opt_public_key.is_none().into() { | ||
return Err(SignatureVerificationError::new( | ||
SignatureVerificationErrorKind::KeyDecodingFailure, | ||
)); | ||
} else { | ||
opt_public_key.unwrap() | ||
// opt_public_key.expect("we should have asserted that the | ||
// contained value is some") | ||
} | ||
}; | ||
|
||
let verifying_key: VerifyingKey = VerifyingKey::from(public_key); | ||
|
||
let signature: Signature = | ||
Signature::try_from(input.decoded_signature.deref()).map_err(|err| { | ||
SignatureVerificationError::new(SignatureVerificationErrorKind::InvalidSignature) | ||
.with_source(err) | ||
})?; | ||
|
||
match signature::Verifier::verify(&verifying_key, &input.signing_input, &signature) { | ||
Ok(()) => Ok(()), | ||
Err(err) => Err(SignatureVerificationError::new( | ||
SignatureVerificationErrorKind::InvalidSignature, | ||
) | ||
.with_source(err)), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
use std::ops::Deref; | ||
|
||
use identity_verification::jwk::JwkParamsEc; | ||
use identity_verification::jws::SignatureVerificationError; | ||
use identity_verification::jws::SignatureVerificationErrorKind; | ||
use identity_verification::jwu::{self}; | ||
use p256::ecdsa::Signature; | ||
use p256::ecdsa::VerifyingKey; | ||
use p256::elliptic_curve::sec1::FromEncodedPoint; | ||
use p256::elliptic_curve::subtle::CtOption; | ||
use p256::EncodedPoint; | ||
use p256::PublicKey; | ||
|
||
/// A verifier that can handle the | ||
/// [`JwsAlgorithm::ES256`](identity_verification::jws::JwsAlgorithm::ES256) | ||
/// algorithm. | ||
#[derive(Debug, Default)] | ||
#[non_exhaustive] | ||
pub struct Secp256R1Verifier {} | ||
|
||
impl Secp256R1Verifier { | ||
/// Verify a JWS signature secured with the | ||
/// [`JwsAlgorithm::ES256`](identity_verification::jws::JwsAlgorithm::ES256) | ||
/// algorithm. | ||
/// | ||
/// This function is useful when one is building a | ||
/// [`JwsVerifier`](identity_verification::jws::JwsVerifier) that | ||
/// handles the | ||
/// [`JwsAlgorithm::ES256`](identity_verification::jws::JwsAlgorithm::ES256) | ||
/// in the same manner as the [`Secp256R1Verifier`] hence extending its | ||
/// capabilities. | ||
/// | ||
/// # Warning | ||
/// | ||
/// This function does not check whether `alg = ES256` in the protected | ||
/// header. Callers are expected to assert this prior to calling the | ||
/// function. | ||
pub fn verify( | ||
input: &identity_verification::jws::VerificationInput, | ||
public_key: &identity_verification::jwk::Jwk, | ||
) -> Result<(), SignatureVerificationError> { | ||
// Obtain a P256 public key. | ||
let params: &JwkParamsEc = public_key | ||
.try_ec_params() | ||
.map_err(|_| SignatureVerificationErrorKind::UnsupportedKeyType)?; | ||
|
||
// Concatenate x and y coordinates as required by | ||
// EncodedPoint::from_untagged_bytes. | ||
let public_key_bytes = jwu::decode_b64(¶ms.x) | ||
.map_err(|err| { | ||
SignatureVerificationError::new(SignatureVerificationErrorKind::KeyDecodingFailure) | ||
.with_source(err) | ||
})? | ||
.into_iter() | ||
.chain(jwu::decode_b64(¶ms.y).map_err(|err| { | ||
SignatureVerificationError::new(SignatureVerificationErrorKind::KeyDecodingFailure) | ||
.with_source(err) | ||
})?) | ||
.collect(); | ||
|
||
// The JWK contains the uncompressed x and y coordinates, so we can create the | ||
// encoded point directly without prefixing an SEC1 tag. | ||
let encoded_point: EncodedPoint = EncodedPoint::from_untagged_bytes(&public_key_bytes); | ||
let public_key: PublicKey = { | ||
let opt_public_key: CtOption<PublicKey> = PublicKey::from_encoded_point(&encoded_point); | ||
if opt_public_key.is_none().into() { | ||
return Err(SignatureVerificationError::new( | ||
SignatureVerificationErrorKind::KeyDecodingFailure, | ||
)); | ||
} else { | ||
opt_public_key.unwrap() | ||
// opt_public_key.expect("we should have asserted that the | ||
// contained value is some") | ||
} | ||
}; | ||
|
||
let verifying_key: VerifyingKey = VerifyingKey::from(public_key); | ||
|
||
let signature: Signature = | ||
Signature::try_from(input.decoded_signature.deref()).map_err(|err| { | ||
SignatureVerificationError::new(SignatureVerificationErrorKind::InvalidSignature) | ||
.with_source(err) | ||
})?; | ||
|
||
match signature::Verifier::verify(&verifying_key, &input.signing_input, &signature) { | ||
Ok(()) => Ok(()), | ||
Err(err) => Err(SignatureVerificationError::new( | ||
SignatureVerificationErrorKind::InvalidSignature, | ||
) | ||
.with_source(err)), | ||
} | ||
} | ||
} |
Oops, something went wrong.