mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Create RC primary service (load balancer)
This commit is contained in:
12
polariton_auth/Cargo.toml
Normal file
12
polariton_auth/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "polariton_auth"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
log.workspace = true
|
||||
polariton.workspace = true
|
||||
num = "0.4"
|
||||
rand = "0.9"
|
||||
ring = "0.17"
|
||||
simple-rijndael = "0.3"
|
||||
86
polariton_auth/src/encryption.rs
Normal file
86
polariton_auth/src/encryption.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
use num::BigInt;
|
||||
use rand::Rng;
|
||||
use simple_rijndael::impls::RijndaelCbc;
|
||||
use simple_rijndael::paddings::Pkcs7Padding;
|
||||
|
||||
pub struct Keys {
|
||||
pub pub_key: Vec<u8>,
|
||||
pub enc: CryptoImpl,
|
||||
}
|
||||
|
||||
const SECRET_LEN: usize = 160 / 8;
|
||||
const PRIME_768: &[u8] = &[255, 255, 255, 255, 255, 255, 255, 255, 201, 15,
|
||||
218, 162, 33, 104, 194, 52, 196, 198, 98, 139,
|
||||
128, 220, 28, 209, 41, 2, 78, 8, 138, 103,
|
||||
204, 116, 2, 11, 190, 166, 59, 19, 155, 34,
|
||||
81, 74, 8, 121, 142, 52, 4, 221, 239, 149,
|
||||
25, 179, 205, 58, 67, 27, 48, 43, 10, 109,
|
||||
242, 95, 20, 55, 79, 225, 53, 109, 109, 81,
|
||||
194, 69, 228, 133, 181, 118, 98, 94, 126, 198,
|
||||
244, 76, 66, 233, 166, 58, 54, 32, 255, 255,
|
||||
255, 255, 255, 255, 255, 255];
|
||||
const PRIME_ROOT: u8 = 22;
|
||||
|
||||
pub fn generate_encryption_details(client_pub_key: &[u8]) -> Keys {
|
||||
let client_num = BigInt::from_bytes_be(num::bigint::Sign::Plus, client_pub_key);
|
||||
let big_0 = BigInt::from(0);
|
||||
let prime_root = BigInt::from(PRIME_ROOT);
|
||||
let my_prime = BigInt::from_bytes_be(num::bigint::Sign::Plus, PRIME_768);
|
||||
log::debug!("Generating keys for client pub key {}", client_num.to_string());
|
||||
let mut rng = rand::rng();
|
||||
let mut bytes = rng.random::<[u8; SECRET_LEN]>();
|
||||
let mut my_secret = BigInt::from_bytes_be(num::bigint::Sign::Plus, &bytes);
|
||||
while my_secret >= &my_prime - 1 || my_secret == big_0 {
|
||||
bytes = rng.random::<[u8; SECRET_LEN]>();
|
||||
my_secret = BigInt::from_bytes_be(num::bigint::Sign::Plus, &bytes);
|
||||
log::debug!("Generated secret {} (prime to beat: {})", my_secret.to_string(), my_prime.to_string());
|
||||
}
|
||||
let my_pub_key = prime_root.modpow(&my_secret, &my_prime);
|
||||
let shared_key = client_num.modpow(&my_secret, &my_prime);
|
||||
log::debug!("Generated shared key {} and pub key {}", shared_key.to_string(), my_pub_key.to_string());
|
||||
let shared_key = shared_key.to_bytes_be().1;
|
||||
let enc_key: Vec<u8> = ring::digest::digest(&ring::digest::SHA256, &shared_key).as_ref().into();
|
||||
log::debug!("Encryption key is {:?}", enc_key.as_slice());
|
||||
Keys {
|
||||
pub_key: my_pub_key.to_signed_bytes_be(),
|
||||
enc: CryptoImpl::new(enc_key),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CryptoImpl {
|
||||
crypto: RijndaelCbc<Pkcs7Padding>,
|
||||
iv: [u8; 16],
|
||||
key: Vec<u8>
|
||||
}
|
||||
|
||||
impl CryptoImpl {
|
||||
fn new(key: Vec<u8>) -> Self {
|
||||
Self {
|
||||
crypto: RijndaelCbc::<Pkcs7Padding>::new(&key, 16).unwrap(),
|
||||
iv: [0u8; 16],
|
||||
key,
|
||||
}
|
||||
}
|
||||
|
||||
fn decrypt(&self, data: Vec<u8>) -> Vec<u8> {
|
||||
self.crypto.decrypt(&self.iv, data).unwrap_or_default()
|
||||
}
|
||||
|
||||
fn encrypt(&self, data: Vec<u8>) -> Vec<u8> {
|
||||
self.crypto.encrypt(&self.iv, data).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl polariton::packet::Cryptographer for CryptoImpl {
|
||||
fn decrypt(&self, data: Vec<u8>) -> Vec<u8> {
|
||||
self.decrypt(data)
|
||||
}
|
||||
|
||||
fn encrypt(&self, data: Vec<u8>) -> Vec<u8> {
|
||||
self.encrypt(data)
|
||||
}
|
||||
|
||||
fn secret(&self) -> &'_ [u8] {
|
||||
self.key.as_slice()
|
||||
}
|
||||
}
|
||||
198
polariton_auth/src/handshake.rs
Normal file
198
polariton_auth/src/handshake.rs
Normal file
@@ -0,0 +1,198 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use polariton::packet::{Packet, Message, StandardMessage, Data, Cryptographer};
|
||||
use polariton::operation::{Typed, ParameterTable, OperationResponse};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Handshake<T> {
|
||||
state: T,
|
||||
}
|
||||
|
||||
pub struct HandshakeAnd<T, X> {
|
||||
pub handshake: Handshake<T>,
|
||||
pub extra: X,
|
||||
}
|
||||
|
||||
pub struct Start<'a> {
|
||||
app_id: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ConnectError<'b, 'a> {
|
||||
UnexpectedPacket,
|
||||
WrongAppId { got: &'b str, expected: &'a str },
|
||||
}
|
||||
|
||||
// TODO impl core::fmt::Display for ConnectError
|
||||
// TODO impl std::error::Error for ConnectError
|
||||
|
||||
impl <'a> Handshake<Start<'a>> {
|
||||
pub fn new(app_id: &'a str) -> Self {
|
||||
Self {
|
||||
state: Start { app_id },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect<'b>(self, packet: &'b Packet) -> Result<HandshakeAnd<Connected, Packet>, HandshakeAnd<Start<'a>, ConnectError<'b, 'a>>> {
|
||||
if let Packet::Packet(packet) = &packet {
|
||||
if let Message::Standard(conn) = &packet.message {
|
||||
if let Data::InitStart(info) = &conn.data {
|
||||
if info.app_id != self.state.app_id {
|
||||
let err = ConnectError::WrongAppId { got: &info.app_id, expected: &self.state.app_id };
|
||||
return Err(HandshakeAnd {
|
||||
handshake: self,
|
||||
extra: err,
|
||||
});
|
||||
}
|
||||
let new_self = Handshake::<Connected> {
|
||||
state: Connected,
|
||||
};
|
||||
return Ok(HandshakeAnd {
|
||||
handshake: new_self,
|
||||
extra: Packet::from_message(
|
||||
Message::Standard(StandardMessage {
|
||||
flags: 0,
|
||||
data: Data::InitAck ,
|
||||
}),
|
||||
packet.header.channel, true, None).unwrap()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(HandshakeAnd {
|
||||
handshake: self,
|
||||
extra: ConnectError::UnexpectedPacket,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Connected;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EncryptError {
|
||||
UnexpectedPacket,
|
||||
MissingParameter(u8),
|
||||
}
|
||||
|
||||
impl Handshake<Connected> {
|
||||
const PUBLIC_KEY_PARAM_KEY: u8 = 1;
|
||||
|
||||
pub fn encrypt<'a>(self, packet: &'a Packet) -> Result<HandshakeAnd<Encrypted, (Packet, Box<Arc<dyn Cryptographer>>)>, HandshakeAnd<Connected, EncryptError>> {
|
||||
if let Packet::Packet(packet) = &packet {
|
||||
if let Message::Standard(conn) = &packet.message {
|
||||
if let Data::InternalOpReq(req) = &conn.data {
|
||||
if req.code == 0 {
|
||||
let params = req.params.to_owned().to_dict();
|
||||
if let Some(Typed::Bytes(pub_key)) = params.get(&Self::PUBLIC_KEY_PARAM_KEY) {
|
||||
let keys = crate::encryption::generate_encryption_details(&pub_key.vec);
|
||||
let mut response_params = std::collections::HashMap::with_capacity(1);
|
||||
response_params.insert(Self::PUBLIC_KEY_PARAM_KEY, Typed::Bytes(keys.pub_key.into()));
|
||||
let resp_packet = Packet::from_message(
|
||||
Message::Standard(
|
||||
StandardMessage {
|
||||
flags: 0,
|
||||
data: Data::InternalOpResp(OperationResponse {
|
||||
code: req.code,
|
||||
return_code: 0,
|
||||
message: Typed::Null,
|
||||
params: ParameterTable::from_dict(response_params) })
|
||||
}), 0, true, None).unwrap();
|
||||
let new_self = Handshake::<Encrypted> {
|
||||
state: Encrypted,
|
||||
};
|
||||
return Ok(HandshakeAnd {
|
||||
handshake: new_self,
|
||||
extra: (resp_packet, Box::new(Arc::new(keys.enc))),
|
||||
});
|
||||
} else {
|
||||
return Err(HandshakeAnd {
|
||||
handshake: self,
|
||||
extra: EncryptError::MissingParameter(Self::PUBLIC_KEY_PARAM_KEY),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(HandshakeAnd {
|
||||
handshake: self,
|
||||
extra: EncryptError::UnexpectedPacket,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Encrypted;
|
||||
|
||||
impl Handshake<Encrypted> {
|
||||
pub fn with_auth<T: AuthProvider<E>, E>(self, auth: T) -> Handshake<Auth<T, E>> {
|
||||
Handshake {
|
||||
state: Auth {
|
||||
authenticator: auth,
|
||||
_e: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AuthProvider<E> {
|
||||
fn validate(&mut self, params: &std::collections::HashMap<u8, Typed>) -> Result<std::collections::HashMap<u8, Typed>, E>;
|
||||
}
|
||||
|
||||
pub struct Auth<T: AuthProvider<E>, E> {
|
||||
authenticator: T,
|
||||
_e: std::marker::PhantomData<E>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AuthError<E> {
|
||||
Validation(E),
|
||||
UnexpectedPacket
|
||||
}
|
||||
|
||||
impl <T: AuthProvider<E>, E> Handshake<Auth<T, E>> {
|
||||
//const SERVER_ADDRESS_KEY: u8 = 230;
|
||||
const AUTH_REQUEST_CODE: u8 = 230;
|
||||
const USER_ID_KEY: u8 = 225;
|
||||
const NICKNAME_KEY: u8 = 225;
|
||||
pub fn authenticate<'a>(mut self, packet: &'a Packet, crypto: Box<Arc<dyn Cryptographer>>) -> Result<Packet, HandshakeAnd<Auth<T, E>, AuthError<E>>> {
|
||||
if let Packet::Packet(packet) = &packet {
|
||||
if let Message::Standard(conn) = &packet.message {
|
||||
if let Data::OpReq(req) = &conn.data {
|
||||
if req.code == Self::AUTH_REQUEST_CODE /* or 231 ???*/ {
|
||||
let req_dict = req.params.to_owned().to_dict();
|
||||
let mut params_resp = match self.state.authenticator.validate(&req_dict) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
return Err(HandshakeAnd {
|
||||
handshake: self,
|
||||
extra: AuthError::Validation(e),
|
||||
});
|
||||
}
|
||||
};
|
||||
if let Some(user_id) = req_dict.get(&Self::USER_ID_KEY) {
|
||||
params_resp.insert(Self::USER_ID_KEY, user_id.to_owned());
|
||||
params_resp.insert(Self::NICKNAME_KEY, user_id.to_owned());
|
||||
}
|
||||
return Ok(Packet::from_message(
|
||||
Message::Standard(
|
||||
StandardMessage {
|
||||
flags: 0,
|
||||
data: Data::OpResp(OperationResponse {
|
||||
code: Self::AUTH_REQUEST_CODE,
|
||||
return_code: 0,
|
||||
message: Typed::Null,
|
||||
params: params_resp.into(),
|
||||
})
|
||||
}.encrypt(conn.is_encrypted())
|
||||
), packet.header.channel, true, Some(crypto)).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(HandshakeAnd {
|
||||
handshake: self,
|
||||
extra: AuthError::UnexpectedPacket,
|
||||
})
|
||||
}
|
||||
}
|
||||
7
polariton_auth/src/lib.rs
Normal file
7
polariton_auth/src/lib.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
mod encryption;
|
||||
|
||||
mod handshake;
|
||||
pub use handshake::{Handshake, AuthProvider, AuthError};
|
||||
|
||||
mod ping_pong;
|
||||
pub use ping_pong::ping_pong;
|
||||
11
polariton_auth/src/ping_pong.rs
Normal file
11
polariton_auth/src/ping_pong.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
use polariton::packet::Ping;
|
||||
|
||||
#[inline]
|
||||
pub fn ping_pong(mut ping: Ping) -> Ping {
|
||||
if ping.tick2.is_some() {
|
||||
ping.tick2 = None;
|
||||
} else {
|
||||
ping.tick2 = Some(ping.tick1);
|
||||
}
|
||||
ping
|
||||
}
|
||||
Reference in New Issue
Block a user