use std::{fmt::Display, str::FromStr};
use ed25519_dalek::{
SigningKey, VerifyingKey,
pkcs8::{
self, DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey, KeypairBytes,
spki::der::pem::LineEnding,
},
};
use jsonwebtoken::{DecodingKey, EncodingKey};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Ed25519VerifyingKeyPem(VerifyingKey);
impl Ed25519VerifyingKeyPem {
pub fn to_decoding_key(&self) -> DecodingKey {
DecodingKey::from_ed_pem(
self.0
.to_public_key_pem(line_ending())
.expect("serialization from a fixed well-defined type")
.as_bytes(),
)
.expect("deserialization from a well-defined serialization")
}
}
impl Display for Ed25519VerifyingKeyPem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
self.0
.to_public_key_pem(line_ending())
.expect("serialization from a fixed well-defined type")
)
}
}
impl From<VerifyingKey> for Ed25519VerifyingKeyPem {
fn from(value: VerifyingKey) -> Self {
Self(value)
}
}
impl Serialize for Ed25519VerifyingKeyPem {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let pem = self
.0
.to_public_key_pem(LineEnding::LF)
.map_err(serde::ser::Error::custom)?;
serializer.serialize_str(&pem)
}
}
impl<'e> Deserialize<'e> for Ed25519VerifyingKeyPem {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'e>,
{
let pem = String::deserialize(deserializer)?;
let key = VerifyingKey::from_public_key_pem(&pem).map_err(serde::de::Error::custom)?;
Ok(Self(key))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Ed25519SigningKeyPem(SigningKey);
impl Ed25519SigningKeyPem {
pub fn warning_to_private_key_pem(&self) -> String {
self.0
.to_pkcs8_pem(line_ending())
.expect("serialization from a fixed well-defined type")
.as_str()
.into()
}
pub fn to_decoding_key(&self) -> DecodingKey {
DecodingKey::from_ed_pem(
self.0
.verifying_key()
.to_public_key_pem(line_ending())
.expect("serialization from a fixed well-defined type")
.as_bytes(),
)
.expect("deserialization from a well-defined serialization")
}
pub fn to_verifying_key(&self) -> VerifyingKey {
self.0.verifying_key()
}
}
impl From<SigningKey> for Ed25519SigningKeyPem {
fn from(value: SigningKey) -> Self {
Self(value)
}
}
impl FromStr for Ed25519SigningKeyPem {
type Err = pkcs8::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(SigningKey::from_pkcs8_pem(s)?))
}
}
impl Serialize for Ed25519SigningKeyPem {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut keypair_bytes: KeypairBytes = self.0.clone().into();
keypair_bytes.public_key = None; let pem = keypair_bytes
.to_pkcs8_pem(line_ending())
.map_err(serde::ser::Error::custom)?;
serializer.serialize_str(&pem)
}
}
impl<'e> Deserialize<'e> for Ed25519SigningKeyPem {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'e>,
{
let pem = String::deserialize(deserializer)?;
let key = SigningKey::from_pkcs8_pem(&pem).map_err(serde::de::Error::custom)?;
Ok(Self(key))
}
}
impl From<&Ed25519SigningKeyPem> for EncodingKey {
fn from(value: &Ed25519SigningKeyPem) -> EncodingKey {
EncodingKey::from_ed_der(value.0.to_pkcs8_der().expect("should not fail").as_bytes())
}
}
fn line_ending() -> LineEnding {
#[cfg(target_family = "windows")]
let line_ending = LineEnding::CRLF;
#[cfg(not(target_family = "windows"))]
let line_ending = LineEnding::LF;
line_ending
}
#[cfg(test)]
mod tests {
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use crate::ed25519::{Ed25519SigningKeyPem, Ed25519VerifyingKeyPem};
#[derive(Debug, Serialize, Deserialize)]
struct ConfigWithVerifyingKey<T> {
verifying_key: T,
}
#[derive(Debug, Serialize, Deserialize)]
struct ConfigWithSigningKey<T> {
signing_key: T,
}
const ED25519_SIGNING_KEY_PEM: &str = include_str!("testdata/ed25519_signing.pem");
const ED25519_VERIFYING_KEY_PEM: &str = include_str!("testdata/ed25519_verifying.pem");
const RSA_PRI_KEY: &str = include_str!("testdata/rsa_private.pem");
const RSA_PUB_KEY: &str = include_str!("testdata/rsa_public.pem");
type TestResult = Result<(), Box<dyn std::error::Error>>;
#[test]
fn parsing_ed25519_signing_key_succeeds() -> TestResult {
assert_parse_succeeds::<_, ConfigWithSigningKey<Ed25519SigningKeyPem>>(
ConfigWithSigningKey {
signing_key: ED25519_SIGNING_KEY_PEM.to_string(),
},
)
}
#[test]
fn parsing_ed25519_verifying_key_succeeds() -> TestResult {
assert_parse_succeeds::<_, ConfigWithVerifyingKey<Ed25519VerifyingKeyPem>>(
ConfigWithVerifyingKey {
verifying_key: ED25519_VERIFYING_KEY_PEM.to_string(),
},
)
}
#[test]
fn parsing_ed25519_verifying_key_as_signing_key_fails() -> TestResult {
assert_parse_fails::<_, ConfigWithVerifyingKey<Ed25519SigningKeyPem>>(
ConfigWithVerifyingKey {
verifying_key: ED25519_VERIFYING_KEY_PEM.to_string(),
},
)
}
#[test]
fn parsing_ed25519_signing_key_as_verifying_key_fails() -> TestResult {
assert_parse_fails::<_, ConfigWithSigningKey<Ed25519VerifyingKeyPem>>(
ConfigWithSigningKey {
signing_key: ED25519_SIGNING_KEY_PEM.to_string(),
},
)
}
#[test]
fn parsing_rsa_public_key_as_ed25519_verifying_key_fails() -> TestResult {
assert_parse_fails::<_, ConfigWithVerifyingKey<Ed25519VerifyingKeyPem>>(
ConfigWithVerifyingKey {
verifying_key: RSA_PUB_KEY.to_string(),
},
)
}
#[test]
fn parsing_rsa_private_key_as_ed25519_signing_key_fails() -> TestResult {
assert_parse_fails::<_, ConfigWithVerifyingKey<Ed25519SigningKeyPem>>(
ConfigWithSigningKey {
signing_key: RSA_PRI_KEY.to_string(),
},
)
}
fn assert_parse_succeeds<T, S>(source_config: T) -> TestResult
where
T: Serialize,
S: Serialize + DeserializeOwned,
{
assert_parse_result::<_, S>(source_config, false)
}
fn assert_parse_fails<T, S>(source_config: T) -> TestResult
where
T: Serialize,
S: Serialize + DeserializeOwned,
{
assert_parse_result::<_, S>(source_config, true)
}
fn assert_parse_result<T, S>(source_config: T, expect_parse_failure: bool) -> TestResult
where
T: Serialize,
S: Serialize + DeserializeOwned,
{
let expected_serialized = serde_json::to_string(&source_config)?;
let deserialized = serde_json::from_str::<S>(&expected_serialized);
let deserialized = if expect_parse_failure {
assert!(deserialized.is_err());
return Ok(());
} else {
deserialized.unwrap()
};
let actual_serialize = serde_json::to_string::<S>(&deserialized)?;
assert_eq!(strip_cr(expected_serialized), strip_cr(actual_serialize));
Ok(())
}
fn strip_cr(s: String) -> String {
s.replace("\\r\\n", "\\n")
}
}