Implemented key parsing
This commit is contained in:
parent
62fc36ff03
commit
88df8eca55
|
@ -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,
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in New Issue