Skip to content

Commit

Permalink
Merge pull request #83 from minkan-chat/82-use-jwk-thumbprints-in-par…
Browse files Browse the repository at this point in the history
…tialeq-implementations

feat: add JsonWebKey::into_verifying_key method & JWK thumbprint calculation
  • Loading branch information
Stupremee authored Nov 4, 2023
2 parents c215eab + 59828f6 commit ab0d001
Show file tree
Hide file tree
Showing 14 changed files with 459 additions and 9 deletions.
2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
with pkgs; {
devShells.default = mkShell {
buildInputs = [
rust-bin.stable.latest.default
rust-bin.nightly.latest.default
step-cli
cargo-audit
cargo-outdated
Expand Down
163 changes: 162 additions & 1 deletion src/jwk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize};

use crate::{
jwa::{EcDSA, JsonWebAlgorithm, JsonWebEncryptionAlgorithm, JsonWebSigningAlgorithm},
jwk::ec::{EcPrivate, EcPublic},
jwk::{
ec::{EcPrivate, EcPublic},
okp::{
curve25519::{Curve25519Private, Curve25519Public},
OkpPrivate, OkpPublic,
},
},
policy::{Checkable, Checked, CryptographicOperation, Policy},
sealed::Sealed,
UntypedAdditionalProperties,
Expand All @@ -30,6 +36,7 @@ mod private;
mod public;
pub(crate) mod serde_impl;
mod signer;
pub(crate) mod thumbprint;
mod verifier;

use self::serde_impl::Base64DerCertificate;
Expand All @@ -43,6 +50,7 @@ pub use self::{
public::Public,
signer::{FromJwkError, JwkSigner},
symmetric::SymmetricJsonWebKey,
thumbprint::Thumbprint,
verifier::JwkVerifier,
};

Expand Down Expand Up @@ -389,6 +397,142 @@ impl<T> JsonWebKey<T> {
pub fn additional(&self) -> &T {
&self.additional
}

/// Checks if this [`JsonWebKey`] is a symmetric key.
#[inline]
pub fn is_symmetric(&self) -> bool {
matches!(self.key_type, JsonWebKeyType::Symmetric(_))
}

/// Checks if this [`JsonWebKey`] is an asymmetric key.
#[inline]
pub fn is_asymmetric(&self) -> bool {
matches!(self.key_type, JsonWebKeyType::Asymmetric(_))
}

/// Checks if this [`JsonWebKey`] can be used for signing.
///
/// For asymmetric keys, this method is equivalent to checking
/// if this key is a private key.
/// For symmetric keys, this always returns `true`.
#[inline]
pub fn is_signing_key(&self) -> bool {
match self.key_type() {
JsonWebKeyType::Symmetric(_) => true,
JsonWebKeyType::Asymmetric(ref key) => match &**key {
AsymmetricJsonWebKey::Private(_) => true,
AsymmetricJsonWebKey::Public(_) => false,
},
}
}

/// Strips the secret material from this [`JsonWebKey`].
///
/// After calling this method, the key can safely be shared as it
/// only contains the public parts.
///
/// For symmetric keys, this method returns [`None`], as symmetric
/// keys will always hold the secret material.
#[doc(alias = "strip_private_material")]
pub fn strip_secret_material(mut self) -> Option<Self> {
let key = match self.key_type {
JsonWebKeyType::Symmetric(_) => return None,
JsonWebKeyType::Asymmetric(ref key) => key,
};

match &**key {
// public keys are no-ops
AsymmetricJsonWebKey::Public(_) => Some(self),
AsymmetricJsonWebKey::Private(Private::Okp(okp)) => {
let key = match okp {
OkpPrivate::Curve25519(Curve25519Private::Ed(key)) => {
OkpPublic::Curve25519(Curve25519Public::Ed(key.to_public_key()))
}
};

self.key_type = JsonWebKeyType::Asymmetric(Box::new(AsymmetricJsonWebKey::Public(
Public::Okp(key),
)));

Some(self)
}
AsymmetricJsonWebKey::Private(Private::Ec(ec)) => {
let pub_key = match ec {
EcPrivate::P256(key) => EcPublic::P256(key.to_public_key()),
EcPrivate::P384(key) => EcPublic::P384(key.to_public_key()),
EcPrivate::Secp256k1(key) => EcPublic::Secp256k1(key.to_public_key()),
};

self.key_type = JsonWebKeyType::Asymmetric(Box::new(AsymmetricJsonWebKey::Public(
Public::Ec(pub_key),
)));

Some(self)
}
AsymmetricJsonWebKey::Private(Private::Rsa(rsa)) => {
let pub_key = Public::Rsa(rsa.to_public_key());

self.key_type =
JsonWebKeyType::Asymmetric(Box::new(AsymmetricJsonWebKey::Public(pub_key)));

Some(self)
}
}
}

/// Converts this [`JsonWebKey`] into a [`JsonWebKey`] that is only meant to
/// be uesd for verifying signatures.
///
/// For asymmetric keys, this operation is equivalent to converting a
/// private key into it's public key.
///
/// For symmetric keys, this operation is a no-op, as the secret material
/// can not be removed from the key.
#[doc(alias = "into_public_key")]
pub fn into_verifying_key(mut self) -> Self {
match self.key_type {
// symmetric keys are no-op
JsonWebKeyType::Symmetric(_) => self,
JsonWebKeyType::Asymmetric(ref key) => match &**key {
// public keys are no-ops too
AsymmetricJsonWebKey::Public(_) => self,
AsymmetricJsonWebKey::Private(Private::Okp(okp)) => {
let key = match okp {
OkpPrivate::Curve25519(Curve25519Private::Ed(key)) => {
OkpPublic::Curve25519(Curve25519Public::Ed(key.to_public_key()))
}
};

self.key_type = JsonWebKeyType::Asymmetric(Box::new(
AsymmetricJsonWebKey::Public(Public::Okp(key)),
));

self
}
AsymmetricJsonWebKey::Private(Private::Ec(ec)) => {
let pub_key = match ec {
EcPrivate::P256(key) => EcPublic::P256(key.to_public_key()),
EcPrivate::P384(key) => EcPublic::P384(key.to_public_key()),
EcPrivate::Secp256k1(key) => EcPublic::Secp256k1(key.to_public_key()),
};

self.key_type = JsonWebKeyType::Asymmetric(Box::new(
AsymmetricJsonWebKey::Public(Public::Ec(pub_key)),
));

self
}
AsymmetricJsonWebKey::Private(Private::Rsa(rsa)) => {
let pub_key = Public::Rsa(rsa.to_public_key());

self.key_type =
JsonWebKeyType::Asymmetric(Box::new(AsymmetricJsonWebKey::Public(pub_key)));

self
}
},
}
}
}

impl<T: Serialize> JsonWebKey<T> {
Expand Down Expand Up @@ -543,6 +687,13 @@ where
}
}

impl Sealed for JsonWebKey {}
impl Thumbprint for JsonWebKey {
fn thumbprint_prehashed(&self) -> String {
self.key_type().thumbprint_prehashed()
}
}

/// A [`JsonWebKey`] represents a cryptographic key. It can either be symmetric
/// or asymmetric. In the latter case, it can store public or private
/// information about the key. This enum represents the key types as defined in
Expand All @@ -558,6 +709,16 @@ pub enum JsonWebKeyType {
Asymmetric(Box<AsymmetricJsonWebKey>),
}

impl Sealed for JsonWebKeyType {}
impl Thumbprint for JsonWebKeyType {
fn thumbprint_prehashed(&self) -> String {
match self {
JsonWebKeyType::Symmetric(key) => key.thumbprint_prehashed(),
JsonWebKeyType::Asymmetric(key) => key.thumbprint_prehashed(),
}
}
}

impl JsonWebKeyType {
pub(self) fn compatible_with(&self, alg: &JsonWebAlgorithm) -> bool {
use JsonWebAlgorithm::*;
Expand Down
14 changes: 13 additions & 1 deletion src/jwk/asymmetric.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use alloc::string::String;

use serde::{Deserialize, Serialize};

use super::{Private, Public};
use super::{Private, Public, Thumbprint};

/// Some kind of asymmetric cryptographic key which can be either [`Private`] or
/// [`Public`]
Expand All @@ -13,6 +15,16 @@ pub enum AsymmetricJsonWebKey {
Public(Public),
}

impl crate::sealed::Sealed for AsymmetricJsonWebKey {}
impl Thumbprint for AsymmetricJsonWebKey {
fn thumbprint_prehashed(&self) -> String {
match self {
AsymmetricJsonWebKey::Private(key) => key.thumbprint_prehashed(),
AsymmetricJsonWebKey::Public(key) => key.thumbprint_prehashed(),
}
}
}

impl From<AsymmetricJsonWebKey> for super::JsonWebKeyType {
fn from(x: AsymmetricJsonWebKey) -> Self {
super::JsonWebKeyType::Asymmetric(alloc::boxed::Box::new(x))
Expand Down
23 changes: 23 additions & 0 deletions src/jwk/ec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use self::{
p384::{P384PrivateKey, P384PublicKey},
secp256k1::{Secp256k1PrivateKey, Secp256k1PublicKey},
};
use super::Thumbprint;
use crate::{base64_url::Base64UrlEncodedField, tagged_visitor::TaggedContentVisitor};

// FIXME: support all curves specified in IANA "JWK Elliptic Curve"
Expand All @@ -41,6 +42,17 @@ pub enum EcPublic {
Secp256k1(Secp256k1PublicKey),
}

impl crate::sealed::Sealed for EcPublic {}
impl Thumbprint for EcPublic {
fn thumbprint_prehashed(&self) -> String {
match self {
EcPublic::P256(key) => key.thumbprint_prehashed(),
EcPublic::P384(key) => key.thumbprint_prehashed(),
EcPublic::Secp256k1(key) => key.thumbprint_prehashed(),
}
}
}

impl From<EcPublic> for super::JsonWebKeyType {
fn from(x: EcPublic) -> Self {
super::JsonWebKeyType::Asymmetric(alloc::boxed::Box::new(
Expand Down Expand Up @@ -74,6 +86,17 @@ pub enum EcPrivate {
Secp256k1(Secp256k1PrivateKey),
}

impl crate::sealed::Sealed for EcPrivate {}
impl Thumbprint for EcPrivate {
fn thumbprint_prehashed(&self) -> String {
match self {
EcPrivate::P256(key) => key.thumbprint_prehashed(),
EcPrivate::P384(key) => key.thumbprint_prehashed(),
EcPrivate::Secp256k1(key) => key.thumbprint_prehashed(),
}
}
}

impl From<EcPrivate> for super::JsonWebKeyType {
fn from(x: EcPrivate) -> Self {
super::JsonWebKeyType::Asymmetric(alloc::boxed::Box::new(
Expand Down
22 changes: 22 additions & 0 deletions src/jwk/okp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ use self::curve25519::{Curve25519Private, Curve25519Public};

pub mod curve25519;

use alloc::string::String;

use serde::{Deserialize, Serialize};

use super::Thumbprint;

/// TODO: unsupported, no implementation available
#[allow(missing_docs)]
pub mod curve448 {
Expand Down Expand Up @@ -49,6 +53,15 @@ pub enum OkpPublic {
// Curve448(Curve448Public),
}

impl crate::sealed::Sealed for OkpPublic {}
impl Thumbprint for OkpPublic {
fn thumbprint_prehashed(&self) -> String {
match self {
OkpPublic::Curve25519(key) => key.thumbprint_prehashed(),
}
}
}

/// The private part of an `OKP` key type
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand All @@ -59,3 +72,12 @@ pub enum OkpPrivate {
// /// `kty` is `OKP` and `crv` is either `Ed448` or `X448`
// Curve448(Curve448Private),
}

impl crate::sealed::Sealed for OkpPrivate {}
impl Thumbprint for OkpPrivate {
fn thumbprint_prehashed(&self) -> String {
match self {
OkpPrivate::Curve25519(key) => key.thumbprint_prehashed(),
}
}
}
21 changes: 21 additions & 0 deletions src/jwk/okp/curve25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
mod ed25519;
mod x25519;

use alloc::string::String;

use serde::{Deserialize, Serialize};

pub use self::ed25519::{Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signer, Ed25519Verifier};
use crate::jwk::Thumbprint;

/// Either a public key for Ed25519 or X25519 (Diffie-Hellman)
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand All @@ -17,6 +20,15 @@ pub enum Curve25519Public {
// X(X25519PublicKey),
}

impl crate::sealed::Sealed for Curve25519Public {}
impl Thumbprint for Curve25519Public {
fn thumbprint_prehashed(&self) -> String {
match self {
Curve25519Public::Ed(key) => key.thumbprint_prehashed(),
}
}
}

/// Either a private key for Ed25519 or X25519 (Diffie-Hellman)
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
Expand All @@ -27,3 +39,12 @@ pub enum Curve25519Private {
// /// Private X25519 part
// X(X25519PrivateKey),
}

impl crate::sealed::Sealed for Curve25519Private {}
impl Thumbprint for Curve25519Private {
fn thumbprint_prehashed(&self) -> String {
match self {
Curve25519Private::Ed(key) => key.thumbprint_prehashed(),
}
}
}
Loading

0 comments on commit ab0d001

Please sign in to comment.