mirror of
https://github.com/clockworklabs/SpacetimeDB.git
synced 2026-05-21 23:22:17 -04:00
118 lines
3.6 KiB
Rust
118 lines
3.6 KiB
Rust
use jsonwebtoken::{DecodingKey, EncodingKey};
|
|
use openssl::ec::{EcGroup, EcKey};
|
|
use openssl::nid::Nid;
|
|
use openssl::pkey::PKey;
|
|
use spacetimedb_paths::cli::{PrivKeyPath, PubKeyPath};
|
|
|
|
use crate::config::CertificateAuthority;
|
|
|
|
pub mod identity;
|
|
pub mod token_validation;
|
|
|
|
/// JWT verification and signing keys.
|
|
#[derive(Clone)]
|
|
pub struct JwtKeys {
|
|
pub public: DecodingKey,
|
|
pub public_pem: Box<[u8]>,
|
|
pub private: EncodingKey,
|
|
pub kid: Option<String>,
|
|
}
|
|
|
|
impl JwtKeys {
|
|
/// Create a new [`JwtKeys`] from paths to the public and private key files
|
|
/// respectively.
|
|
///
|
|
/// The key files must be PEM encoded ECDSA P256 keys.
|
|
pub fn new(public_pem: impl Into<Box<[u8]>>, private_pem: &[u8]) -> anyhow::Result<Self> {
|
|
let public_pem = public_pem.into();
|
|
let public = DecodingKey::from_ec_pem(&public_pem)?;
|
|
let private = EncodingKey::from_ec_pem(private_pem)?;
|
|
|
|
Ok(Self {
|
|
public,
|
|
private,
|
|
public_pem,
|
|
kid: None,
|
|
})
|
|
}
|
|
|
|
pub fn generate() -> anyhow::Result<Self> {
|
|
let keypair = EcKeyPair::generate()?;
|
|
keypair.try_into()
|
|
}
|
|
}
|
|
|
|
// Get the key pair if the given files exist. If they don't, create them.
|
|
// If only one of the files exists, return an error.
|
|
pub fn get_or_create_keys(certs: &CertificateAuthority) -> anyhow::Result<JwtKeys> {
|
|
let public_key_path = &certs.jwt_pub_key_path;
|
|
let private_key_path = &certs.jwt_priv_key_path;
|
|
|
|
let public_key_bytes = public_key_path.read().ok();
|
|
let private_key_bytes = private_key_path.read().ok();
|
|
|
|
// If both keys are unspecified, create them
|
|
let key_pair = match (public_key_bytes, private_key_bytes) {
|
|
(Some(pub_), Some(priv_)) => EcKeyPair::new(pub_, priv_),
|
|
(None, None) => {
|
|
let keys = EcKeyPair::generate()?;
|
|
keys.write_to_files(public_key_path, private_key_path)?;
|
|
keys
|
|
}
|
|
(None, Some(_)) => anyhow::bail!("Unable to read public key for JWT token verification"),
|
|
(Some(_), None) => anyhow::bail!("Unable to read private key for JWT token signing"),
|
|
};
|
|
|
|
key_pair.try_into()
|
|
}
|
|
|
|
// An Ec key pair in pem format.
|
|
pub struct EcKeyPair {
|
|
pub public_key_bytes: Vec<u8>,
|
|
pub private_key_bytes: Vec<u8>,
|
|
}
|
|
|
|
impl TryFrom<EcKeyPair> for JwtKeys {
|
|
type Error = anyhow::Error;
|
|
fn try_from(pair: EcKeyPair) -> anyhow::Result<Self> {
|
|
JwtKeys::new(pair.public_key_bytes, &pair.private_key_bytes)
|
|
}
|
|
}
|
|
|
|
impl EcKeyPair {
|
|
pub fn new(public_key_bytes: Vec<u8>, private_key_bytes: Vec<u8>) -> Self {
|
|
Self {
|
|
public_key_bytes,
|
|
private_key_bytes,
|
|
}
|
|
}
|
|
|
|
pub fn generate() -> anyhow::Result<Self> {
|
|
// Create a new EC group from a named curve.
|
|
let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1)?;
|
|
|
|
// Create a new EC key with the specified group.
|
|
let eckey = EcKey::generate(&group)?;
|
|
|
|
// Create a new PKey from the EC key.
|
|
let pkey = PKey::from_ec_key(eckey.clone())?;
|
|
|
|
// Get the private key in PKCS#8 PEM format & write it.
|
|
let private_key_bytes = pkey.private_key_to_pem_pkcs8()?;
|
|
|
|
// Get the public key in PEM format & write it.
|
|
let public_key_bytes = eckey.public_key_to_pem()?;
|
|
|
|
Ok(Self {
|
|
public_key_bytes,
|
|
private_key_bytes,
|
|
})
|
|
}
|
|
|
|
pub fn write_to_files(&self, public_key_path: &PubKeyPath, private_key_path: &PrivKeyPath) -> anyhow::Result<()> {
|
|
public_key_path.write(&self.public_key_bytes)?;
|
|
private_key_path.write(&self.private_key_bytes)?;
|
|
Ok(())
|
|
}
|
|
}
|