Implemented key parsing

This commit is contained in:
Natty 2024-11-12 22:00:37 +01:00
parent 62fc36ff03
commit 88df8eca55
Signed by: natty
GPG Key ID: BF6CB659ADEE60EC
2 changed files with 130 additions and 19 deletions

View File

@ -11,9 +11,10 @@ use url::Url;
use magnetar_core::web_model::content_type::ContentActivityStreams; use magnetar_core::web_model::content_type::ContentActivityStreams;
use crate::{ use crate::{
ApClientService, client::federation_client::{FederationClient, FederationClientError},
ApSignature, crypto::{ApSigningError, ApSigningKey, SigningAlgorithm},
ApSigningField, ApSigningHeaders, client::federation_client::{FederationClient, FederationClientError}, crypto::{ApSigningError, ApSigningKey, SigningAlgorithm}, SigningInput, SigningParts, ApClientService, ApSignature, ApSigningField,
ApSigningHeaders, SigningInput, SigningParts,
}; };
pub struct ApClientServiceDefaultProvider { pub struct ApClientServiceDefaultProvider {
@ -414,9 +415,9 @@ mod test {
use crate::{ use crate::{
ap_client::ApClientServiceDefaultProvider, ap_client::ApClientServiceDefaultProvider,
ApClientService,
client::federation_client::FederationClient, client::federation_client::FederationClient,
crypto::{ApHttpPrivateKey, SigningAlgorithm}, crypto::{ApHttpPrivateKey, SigningAlgorithm},
ApClientService,
}; };
#[tokio::test] #[tokio::test]
@ -440,7 +441,7 @@ mod test {
let val = ap_client let val = ap_client
.signed_get( .signed_get(
ApHttpPrivateKey::Rsa(Box::new(Cow::Owned(rsa_key))) ApHttpPrivateKey::Rsa(Cow::Owned(Box::new(rsa_key)))
.create_signing_key(&key_id, SigningAlgorithm::RsaSha256) .create_signing_key(&key_id, SigningAlgorithm::RsaSha256)
.into_diagnostic()?, .into_diagnostic()?,
SigningAlgorithm::RsaSha256, SigningAlgorithm::RsaSha256,

View File

@ -1,12 +1,16 @@
use std::{borrow::Cow, fmt::Display}; use rsa::pkcs1::DecodeRsaPrivateKey;
use rsa::pkcs1::DecodeRsaPublicKey;
use rsa::pkcs8::DecodePrivateKey;
use rsa::pkcs8::DecodePublicKey;
use rsa::signature::Verifier; use rsa::signature::Verifier;
use rsa::{ use rsa::{
sha2::{Sha256, Sha512}, sha2::{Sha256, Sha512},
signature::Signer, signature::Signer,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Formatter;
use std::str::FromStr;
use std::{borrow::Cow, fmt::Display};
use strum::AsRefStr; use strum::AsRefStr;
use thiserror::Error; use thiserror::Error;
@ -35,7 +39,7 @@ pub enum SigningAlgorithm {
} }
impl Display for SigningAlgorithm { impl Display for SigningAlgorithm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
Self::Hs2019 => write!(f, "hs2019"), Self::Hs2019 => write!(f, "hs2019"),
Self::RsaSha256 => write!(f, "rsa-sha256"), Self::RsaSha256 => write!(f, "rsa-sha256"),
@ -61,6 +65,59 @@ pub enum ApHttpPublicKey<'a> {
Ed25519(Cow<'a, ed25519_dalek::VerifyingKey>), Ed25519(Cow<'a, ed25519_dalek::VerifyingKey>),
} }
#[derive(Debug, Copy, Clone, Error)]
pub struct ApHttpPublicKeyParseError;
impl Display for ApHttpPublicKeyParseError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Failed to parse the public key: No available parser could decode the PEM string"
)
}
}
impl FromStr for ApHttpPublicKey<'_> {
type Err = ApHttpPublicKeyParseError;
fn from_str(input_pem: &str) -> Result<Self, Self::Err> {
let pem = input_pem.trim();
let parse_pkcs1_rsa: &dyn Fn(_) -> _ = &|p| {
Some(ApHttpPublicKey::Rsa(Cow::Owned(
rsa::RsaPublicKey::from_pkcs1_pem(p).ok()?,
)))
};
let parse_spki_rsa: &dyn Fn(_) -> _ = &|p| {
Some(ApHttpPublicKey::Rsa(Cow::Owned(
rsa::RsaPublicKey::from_public_key_pem(p).ok()?,
)))
};
let parse_spki_ed25519: &dyn Fn(_) -> _ = &|p| {
Some(ApHttpPublicKey::Ed25519(Cow::Owned(
ed25519_dalek::VerifyingKey::from_public_key_pem(p).ok()?,
)))
};
// Some heuristics
let parsers: &[_] = match pem {
p if p.starts_with("-----BEGIN PUBLIC KEY-----") => {
&[parse_spki_rsa, parse_spki_ed25519]
}
p if p.starts_with("-----BEGIN RSA PUBLIC KEY-----") => &[parse_pkcs1_rsa],
_ => &[parse_spki_rsa, parse_spki_ed25519, parse_pkcs1_rsa],
};
for parser in parsers {
if let Some(k) = parser(pem) {
return Ok(k);
}
}
Err(ApHttpPublicKeyParseError)
}
}
impl ApHttpVerificationKey<'_> { impl ApHttpVerificationKey<'_> {
pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), ApVerificationError> { pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), ApVerificationError> {
match self { match self {
@ -109,12 +166,10 @@ impl ApHttpPublicKey<'_> {
)); ));
Ok(verification_key.verify(message, signature)?) Ok(verification_key.verify(message, signature)?)
} }
(_, SigningAlgorithm::RsaSha256) => { (_, SigningAlgorithm::RsaSha256) => Err(ApVerificationError::KeyAlgorithmMismatch(
return Err(ApVerificationError::KeyAlgorithmMismatch(
algorithm, algorithm,
self.as_ref().to_owned(), self.as_ref().to_owned(),
)); )),
}
(Self::Ed25519(key), SigningAlgorithm::Hs2019) => { (Self::Ed25519(key), SigningAlgorithm::Hs2019) => {
let verification_key = ApHttpVerificationKey::Ed25519(Cow::Borrowed(key.as_ref())); let verification_key = ApHttpVerificationKey::Ed25519(Cow::Borrowed(key.as_ref()));
Ok(verification_key.verify(message, signature)?) Ok(verification_key.verify(message, signature)?)
@ -126,17 +181,72 @@ impl ApHttpPublicKey<'_> {
#[derive(Debug, Clone, AsRefStr)] #[derive(Debug, Clone, AsRefStr)]
pub enum ApHttpPrivateKey<'a> { pub enum ApHttpPrivateKey<'a> {
#[strum(serialize = "rsa")] #[strum(serialize = "rsa")]
Rsa(Box<Cow<'a, rsa::RsaPrivateKey>>), Rsa(Cow<'a, Box<rsa::RsaPrivateKey>>),
#[strum(serialize = "ed25519")] #[strum(serialize = "ed25519")]
Ed25519(Cow<'a, ed25519_dalek::SecretKey>), Ed25519(Cow<'a, ed25519_dalek::SecretKey>),
} }
#[derive(Debug, Copy, Clone, Error)]
pub struct ApHttpPrivateKeyParseError;
impl Display for ApHttpPrivateKeyParseError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Failed to parse the private key: No available parser could decode the PEM string"
)
}
}
impl FromStr for ApHttpPrivateKey<'_> {
type Err = ApHttpPrivateKeyParseError;
fn from_str(input_pem: &str) -> Result<Self, Self::Err> {
let pem = input_pem.trim();
let parse_pkcs1_rsa: &dyn Fn(_) -> _ = &|p| {
Some(ApHttpPrivateKey::Rsa(Cow::Owned(Box::new(
rsa::RsaPrivateKey::from_pkcs1_pem(p).ok()?,
))))
};
let parse_pkcs8_rsa: &dyn Fn(_) -> _ = &|p| {
Some(ApHttpPrivateKey::Rsa(Cow::Owned(Box::new(
rsa::RsaPrivateKey::from_pkcs8_pem(p).ok()?,
))))
};
let parse_pkcs8_ed25519: &dyn Fn(_) -> _ = &|p| {
Some(ApHttpPrivateKey::Ed25519(Cow::Owned(
ed25519_dalek::SigningKey::from_pkcs8_pem(p)
.ok()?
.to_bytes(),
)))
};
// Some heuristics
let parsers: &[_] = match pem {
p if p.contains("-----BEGIN PRIVATE KEY-----") => {
&[parse_pkcs8_rsa, parse_pkcs8_ed25519]
}
p if p.contains("-----BEGIN RSA PRIVATE KEY-----") => &[parse_pkcs1_rsa],
_ => &[parse_pkcs8_rsa, parse_pkcs8_ed25519, parse_pkcs1_rsa],
};
for parser in parsers {
if let Some(k) = parser(pem) {
return Ok(k);
}
}
Err(ApHttpPrivateKeyParseError)
}
}
#[derive(Debug, Clone, AsRefStr)] #[derive(Debug, Clone, AsRefStr)]
pub enum ApHttpSigningKey<'a> { pub enum ApHttpSigningKey<'a> {
#[strum(serialize = "rsa-sha256")] #[strum(serialize = "rsa-sha256")]
RsaSha256(Cow<'a, rsa::pkcs1v15::SigningKey<rsa::sha2::Sha256>>), RsaSha256(Cow<'a, rsa::pkcs1v15::SigningKey<Sha256>>),
#[strum(serialize = "rsa-sha512")] #[strum(serialize = "rsa-sha512")]
RsaSha512(Cow<'a, rsa::pkcs1v15::SigningKey<rsa::sha2::Sha512>>), RsaSha512(Cow<'a, rsa::pkcs1v15::SigningKey<Sha512>>),
#[strum(serialize = "ed25519")] #[strum(serialize = "ed25519")]
Ed25519(Cow<'a, ed25519_dalek::SigningKey>), Ed25519(Cow<'a, ed25519_dalek::SigningKey>),
} }
@ -192,7 +302,7 @@ impl ApHttpPrivateKey<'_> {
key: match (self, algorithm) { key: match (self, algorithm) {
(Self::Rsa(key), SigningAlgorithm::RsaSha256 | SigningAlgorithm::Hs2019) => { (Self::Rsa(key), SigningAlgorithm::RsaSha256 | SigningAlgorithm::Hs2019) => {
ApHttpSigningKey::RsaSha256(Cow::Owned(rsa::pkcs1v15::SigningKey::new( ApHttpSigningKey::RsaSha256(Cow::Owned(rsa::pkcs1v15::SigningKey::new(
key.clone().into_owned(), *key.as_ref().to_owned(),
))) )))
} }
(Self::Ed25519(key), SigningAlgorithm::Hs2019) => ApHttpSigningKey::Ed25519( (Self::Ed25519(key), SigningAlgorithm::Hs2019) => ApHttpSigningKey::Ed25519(