use std::{
collections::BTreeMap,
time::{Duration, SystemTime, UNIX_EPOCH},
};
use ed25519_dalek::{
SigningKey,
pkcs8::{EncodePrivateKey, EncodePublicKey},
};
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header};
use pem::Pem;
use rand::RngCore;
use scion_sdk_common_types::ed25519::Ed25519SigningKeyPem;
use serde::{Deserialize, Serialize};
use snap_tokens::{Pssid, session_token::SessionTokenClaims};
use super::manager::{SessionManager, SessionOpenError, SessionTokenError, TokenIssuer};
use crate::state::{DataPlaneId, Id};
pub mod dto;
const DEFAULT_SESSION_DURATION: Duration = Duration::from_secs(3600);
#[derive(Debug, Ord, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Clone)]
pub struct SessionId {
pssid: Pssid,
data_plane_id: DataPlaneId,
}
impl SessionId {
pub fn new(pssid: Pssid, data_plane_id: DataPlaneId) -> Self {
Self {
pssid,
data_plane_id,
}
}
}
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Clone)]
pub struct SessionManagerState {
session_duration: Duration,
sessions: BTreeMap<SessionId, Session>,
}
impl SessionManagerState {
pub fn new(session_duration: Duration) -> Self {
Self {
session_duration,
sessions: BTreeMap::new(),
}
}
}
impl Default for SessionManagerState {
fn default() -> Self {
Self::new(DEFAULT_SESSION_DURATION)
}
}
pub struct SessionGrant {
expiry: SystemTime,
}
impl SessionManager for SessionManagerState {
fn open(
&mut self,
pssid: Pssid,
data_plane_id: DataPlaneId,
) -> Result<SessionGrant, SessionOpenError> {
let session_id = SessionId::new(pssid.clone(), data_plane_id);
let session_expiry = SystemTime::now() + self.session_duration;
let _res = self
.sessions
.insert(session_id, Session::new(session_expiry));
Ok(SessionGrant {
expiry: session_expiry,
})
}
}
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Clone)]
pub struct Session {
expiry: SystemTime,
}
impl Session {
fn new(expiry: SystemTime) -> Self {
Self { expiry }
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct SessionTokenIssuerState {
key: Ed25519SigningKeyPem,
}
impl SessionTokenIssuerState {
pub fn new(key: Ed25519SigningKeyPem) -> Self {
Self { key }
}
}
impl TokenIssuer for SessionTokenIssuerState {
fn issue(
&self,
pssid: Pssid,
data_plane_id: DataPlaneId,
session_grant: SessionGrant,
) -> Result<String, SessionTokenError> {
let claims = SessionTokenClaims {
pssid,
data_plane_id: data_plane_id.as_usize(),
exp: session_grant
.expiry
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs(),
};
let encoding_key = (&self.key).into();
let token = jsonwebtoken::encode(&Header::new(Algorithm::EdDSA), &claims, &encoding_key)
.map_err(SessionTokenError::EncodingError)?;
Ok(token)
}
}
pub fn insecure_const_session_key_pair(input: usize) -> (EncodingKey, DecodingKey) {
let (private_pem, public_pem) = insecure_const_session_key_pair_pem(input);
let encoding_key = EncodingKey::from_ed_pem(pem::encode(&private_pem).as_bytes()).unwrap();
let decoding_key = DecodingKey::from_ed_pem(pem::encode(&public_pem).as_bytes()).unwrap();
(encoding_key, decoding_key)
}
pub fn insecure_const_session_key_pair_pem(input: usize) -> (Pem, Pem) {
let dalek_keypair = insecure_const_ed25519_signing_key(input);
let public_key =
ed25519_dalek::pkcs8::PublicKeyBytes(*dalek_keypair.verifying_key().as_bytes());
let kp = ed25519_dalek::pkcs8::KeypairBytes {
secret_key: *dalek_keypair.as_bytes(),
public_key: Some(public_key),
};
let private_pem = pem::Pem::new("PRIVATE KEY", kp.to_pkcs8_der().unwrap().as_bytes());
let public_pem = pem::Pem::new(
"PUBLIC KEY",
public_key.to_public_key_der().unwrap().as_bytes(),
);
(private_pem, public_pem)
}
pub fn insecure_const_ed25519_signing_key(input: usize) -> SigningKey {
let mut seed = [43u8; 32];
let id_bytes = input.to_le_bytes();
seed[..id_bytes.len()].copy_from_slice(&id_bytes);
ed25519_dalek::SigningKey::from_bytes(&seed)
}
pub fn random_ed25519_signing_key() -> SigningKey {
let mut trng = rand::rng();
let mut seed = [0u8; 32];
trng.fill_bytes(&mut seed[..]);
ed25519_dalek::SigningKey::from_bytes(&seed)
}
#[cfg(test)]
mod tests {
use std::time::{Duration, UNIX_EPOCH};
use scion_sdk_token_validator::validator::{TokenValidator, Validator};
use snap_tokens::snap_token::SnapTokenClaims;
use test_log::test;
use uuid::Uuid;
use super::*;
#[test]
fn session_mgmt() {
let claims = SnapTokenClaims {
pssid: Pssid(Uuid::new_v4()),
exp: (SystemTime::now() + Duration::from_secs(360))
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs(),
};
let dp_id = DataPlaneId::from_usize(0);
let signing_key = insecure_const_ed25519_signing_key(0);
let signing_key = Ed25519SigningKeyPem::from(signing_key);
let decoding_key = signing_key.to_decoding_key();
let issuer = SessionTokenIssuerState::new(signing_key);
let mut session_manager = SessionManagerState::default();
let session_grant = session_manager.open(claims.pssid.clone(), dp_id).unwrap();
let session_token = issuer
.issue(claims.pssid.clone(), dp_id, session_grant)
.unwrap();
Validator::<SessionTokenClaims>::new(decoding_key, None)
.validate(SystemTime::now(), &session_token)
.expect("validation failed");
let _ = session_manager
.open(claims.pssid.clone(), dp_id)
.expect("open second session");
}
}