use ed25519_dalek::{Verifier, Signer, SigningKey, Signature, VerifyingKey};
use rand::rngs::OsRng;
use rand::RngCore;
use crate::hash_str;
use hashbrown::HashSet;
use crate::*;
#[cfg(feature = "wasm")]
use web_sys;
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;
#[cfg(feature = "wasm")]
use wasm_bindgen::JsValue;
#[cfg(feature = "wasm")]
use web_sys::{Crypto, Window,console};
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub enum Capability {
PinCPUCore(u64) , AvailableCores(u64), GPGPURead, GPGPUWrite, InputArguments, StdOut, StdIn, StdErr, CoreDatabaseRead, CoreDatabaseWrite, DownloadDependencies, LoadDependencies, CoreNetworkRead, CoreNetworkWrite, NetworkRead, NetworkWrite, FileSystemRead, FileSystemWrite, FileRead(String), FileWrite(String), AllTablesRead, AllTablesWrite, TableRead((TableId,u64,u64)), TableWrite((TableId,u64,u64)), UserDefined(u64), }
#[derive(Clone)]
pub struct CapabilityToken {
id: u64,
name: String,
capabilities: HashSet<Capability>,
owner: u64,
expires: Option<u64>,
signature: Option<(Signature,VerifyingKey)>, }
impl CapabilityToken {
pub fn new(
name: String,
capabilities: HashSet<Capability>,
owner: u64,
expires: Option<u64>) -> CapabilityToken {
let data = format!("{:?}{:?}{:?}", &name, &owner, &capabilities);
let id = hash_str(&data);
CapabilityToken {
id,
name,
capabilities,
owner,
expires,
signature: None,
}
}
pub fn sign(&mut self, signing_key: &SigningKey ) -> Result<(),MechError> {
match self.signature {
Some(s) => { Err(MechError{tokens: vec![], msg: "".to_string(), id: 3295, kind: MechErrorKind::GenericError(format!("Capability already signed"))})},
None => {
let data_str = format!("{:?}{:?}{:?}", &self.name, &self.owner, &self.capabilities);
let data_bytes = data_str.as_bytes();
let signature = signing_key.sign(&data_bytes);
self.signature = Some((signature,signing_key.verifying_key()));
Ok(())
}
}
}
pub fn verify(&self) -> Result<(),MechError> {
match self.signature {
Some((s,public_key)) => {
let data_str = format!("{:?}{:?}{:?}", &self.name, &self.owner, &self.capabilities);
let data_bytes = data_str.as_bytes();
if public_key.verify(&data_bytes, &s).is_ok() {
Ok(())
} else {
Err(MechError{tokens: vec![], id: 83820, msg: "".to_string(), kind: MechErrorKind::InvalidCapabilityToken})
}
},
None => Err(MechError{tokens: vec![], id: 83821, msg: "".to_string(), kind: MechErrorKind::InvalidCapabilityToken})
}
}
pub fn has_capability(&self, capability: &Capability) -> Result<(),MechError> {
if self.capabilities.contains(capability) {
Ok(())
} else {
Err(MechError{tokens: vec![], id: 83822, msg: "".to_string(), kind: MechErrorKind::MissingCapability(capability.clone())})
}
}
pub fn verify_capability(&self, capability: &Capability) -> Result<(),MechError> {
match self.verify() {
Ok(()) => self.has_capability(capability),
x => x,
}
}
pub fn revoke(&mut self) {
self.expires = None;
self.signature = None;
}
}
impl fmt::Debug for CapabilityToken {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for c in self.capabilities.iter() {
write!(f,"{:?}\n",c)?;
}
Ok(())
}
}
#[cfg(not(feature = "wasm"))]
pub fn generate_uuid() -> u64 {
OsRng.next_u64()
}
#[cfg(feature = "wasm")]
pub fn generate_uuid() -> u64 {
let mut rng = WebCryptoRng{};
rng.next_u64()
}
#[cfg(not(feature = "wasm"))]
pub fn generate_keypair() -> SigningKey {
let mut csprng = OsRng{};
SigningKey::generate(&mut csprng)
}
#[cfg(feature = "wasm")]
pub fn generate_keypair() -> SigningKey {
let window = web_sys::window();
let mut csprng = WebCryptoRng{};
SigningKey::generate(&mut csprng)
}
#[cfg(feature = "wasm")]
struct WebCryptoRng{}
#[cfg(feature = "wasm")]
impl rand_core::CryptoRng for WebCryptoRng{}
#[cfg(feature = "wasm")]
impl rand_core::RngCore for WebCryptoRng {
fn next_u32(&mut self) -> u32{
let mut buf:[u8;4] = [0u8;4];
self.fill_bytes(&mut buf);
u32::from_le_bytes(buf)
}
fn next_u64(&mut self) -> u64{
let mut buf:[u8;8] = [0u8;8];
self.fill_bytes(&mut buf);
u64::from_le_bytes(buf)
}
fn fill_bytes(&mut self, dest: &mut [u8]){
let window = web_sys::window().unwrap();
let crypto = window.crypto().unwrap();
crypto.get_random_values_with_u8_array(dest);
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error>{
let window = web_sys::window().unwrap();
let crypto = window.crypto().unwrap();
crypto.get_random_values_with_u8_array(dest).unwrap();
Ok(())
}
}