use std::fmt;
use std::sync::Arc;
use ed25519_dalek::{SigningKey, VerifyingKey, pkcs8::EncodePrivateKey, pkcs8::EncodePublicKey};
use pkcs8::LineEnding;
use uselesskey_core::negative::CorruptPem;
use uselesskey_core::sink::TempArtifact;
use uselesskey_core::srp::keypair_material::Pkcs8SpkiKeyMaterial;
use uselesskey_core::{Error, Factory};
use crate::Ed25519Spec;
pub const DOMAIN_ED25519_KEYPAIR: &str = "uselesskey:ed25519:keypair";
#[derive(Clone)]
pub struct Ed25519KeyPair {
factory: Factory,
label: String,
spec: Ed25519Spec,
inner: Arc<Inner>,
}
struct Inner {
_private: SigningKey,
#[cfg_attr(not(feature = "jwk"), allow(dead_code))]
public: VerifyingKey,
material: Pkcs8SpkiKeyMaterial,
#[cfg_attr(not(feature = "jwk"), allow(dead_code))]
secret_bytes: [u8; 32],
}
impl fmt::Debug for Ed25519KeyPair {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Ed25519KeyPair")
.field("label", &self.label)
.field("spec", &self.spec)
.finish_non_exhaustive()
}
}
pub trait Ed25519FactoryExt {
fn ed25519(&self, label: impl AsRef<str>, spec: Ed25519Spec) -> Ed25519KeyPair;
}
impl Ed25519FactoryExt for Factory {
fn ed25519(&self, label: impl AsRef<str>, spec: Ed25519Spec) -> Ed25519KeyPair {
Ed25519KeyPair::new(self.clone(), label.as_ref(), spec)
}
}
impl Ed25519KeyPair {
fn new(factory: Factory, label: &str, spec: Ed25519Spec) -> Self {
let inner = load_inner(&factory, label, spec, "good");
Self {
factory,
label: label.to_string(),
spec,
inner,
}
}
fn load_variant(&self, variant: &str) -> Arc<Inner> {
load_inner(&self.factory, &self.label, self.spec, variant)
}
pub fn spec(&self) -> Ed25519Spec {
self.spec
}
pub fn label(&self) -> &str {
&self.label
}
pub fn private_key_pkcs8_der(&self) -> &[u8] {
self.inner.material.private_key_pkcs8_der()
}
pub fn private_key_pkcs8_pem(&self) -> &str {
self.inner.material.private_key_pkcs8_pem()
}
pub fn public_key_spki_der(&self) -> &[u8] {
self.inner.material.public_key_spki_der()
}
pub fn public_key_spki_pem(&self) -> &str {
self.inner.material.public_key_spki_pem()
}
pub fn write_private_key_pkcs8_pem(&self) -> Result<TempArtifact, Error> {
self.inner.material.write_private_key_pkcs8_pem()
}
pub fn write_public_key_spki_pem(&self) -> Result<TempArtifact, Error> {
self.inner.material.write_public_key_spki_pem()
}
pub fn private_key_pkcs8_pem_corrupt(&self, how: CorruptPem) -> String {
self.inner.material.private_key_pkcs8_pem_corrupt(how)
}
pub fn private_key_pkcs8_pem_corrupt_deterministic(&self, variant: &str) -> String {
self.inner
.material
.private_key_pkcs8_pem_corrupt_deterministic(variant)
}
pub fn private_key_pkcs8_der_truncated(&self, len: usize) -> Vec<u8> {
self.inner.material.private_key_pkcs8_der_truncated(len)
}
pub fn private_key_pkcs8_der_corrupt_deterministic(&self, variant: &str) -> Vec<u8> {
self.inner
.material
.private_key_pkcs8_der_corrupt_deterministic(variant)
}
pub fn mismatched_public_key_spki_der(&self) -> Vec<u8> {
let other = self.load_variant("mismatch");
other.material.public_key_spki_der().to_vec()
}
#[cfg(feature = "jwk")]
pub fn kid(&self) -> String {
self.inner.material.kid()
}
#[cfg(feature = "jwk")]
pub fn public_key_jwk(&self) -> uselesskey_jwk::PublicJwk {
self.public_jwk()
}
#[cfg(feature = "jwk")]
pub fn public_jwk(&self) -> uselesskey_jwk::PublicJwk {
use base64::Engine as _;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use uselesskey_jwk::{OkpPublicJwk, PublicJwk};
let x = self.inner.public.as_bytes();
PublicJwk::Okp(OkpPublicJwk {
kty: "OKP",
crv: "Ed25519",
use_: "sig",
alg: "EdDSA",
kid: self.kid(),
x: URL_SAFE_NO_PAD.encode(x),
})
}
#[cfg(feature = "jwk")]
pub fn private_key_jwk(&self) -> uselesskey_jwk::PrivateJwk {
use base64::Engine as _;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use uselesskey_jwk::{OkpPrivateJwk, PrivateJwk};
let x = self.inner.public.as_bytes();
let d = &self.inner.secret_bytes;
PrivateJwk::Okp(OkpPrivateJwk {
kty: "OKP",
crv: "Ed25519",
use_: "sig",
alg: "EdDSA",
kid: self.kid(),
x: URL_SAFE_NO_PAD.encode(x),
d: URL_SAFE_NO_PAD.encode(d),
})
}
#[cfg(feature = "jwk")]
pub fn public_jwks(&self) -> uselesskey_jwk::Jwks {
use uselesskey_jwk::JwksBuilder;
let mut builder = JwksBuilder::new();
builder.push_public(self.public_jwk());
builder.build()
}
#[cfg(feature = "jwk")]
pub fn public_jwk_json(&self) -> serde_json::Value {
self.public_jwk().to_value()
}
#[cfg(feature = "jwk")]
pub fn public_jwks_json(&self) -> serde_json::Value {
self.public_jwks().to_value()
}
#[cfg(feature = "jwk")]
pub fn private_key_jwk_json(&self) -> serde_json::Value {
self.private_key_jwk().to_value()
}
}
fn load_inner(factory: &Factory, label: &str, spec: Ed25519Spec, variant: &str) -> Arc<Inner> {
let spec_bytes = spec.stable_bytes();
factory.get_or_init(
DOMAIN_ED25519_KEYPAIR,
label,
&spec_bytes,
variant,
|seed| {
let mut secret_bytes = [0u8; 32];
seed.fill_bytes(&mut secret_bytes);
let private = SigningKey::from_bytes(&secret_bytes);
let public = private.verifying_key();
let pkcs8_der_doc = private
.to_pkcs8_der()
.expect("failed to encode Ed25519 private key as PKCS#8 DER");
let pkcs8_der: Arc<[u8]> = Arc::from(pkcs8_der_doc.as_bytes());
let pkcs8_pem = private
.to_pkcs8_pem(LineEnding::LF)
.expect("failed to encode Ed25519 private key as PKCS#8 PEM")
.to_string();
let spki_der_doc = public
.to_public_key_der()
.expect("failed to encode Ed25519 public key as SPKI DER");
let spki_der: Arc<[u8]> = Arc::from(spki_der_doc.as_ref());
let spki_pem = public
.to_public_key_pem(LineEnding::LF)
.expect("failed to encode Ed25519 public key as SPKI PEM");
let material = Pkcs8SpkiKeyMaterial::new(pkcs8_der, pkcs8_pem, spki_der, spki_pem);
Inner {
_private: private,
public,
material,
secret_bytes,
}
},
)
}