#![no_std]
extern crate no_std_compat as std;
extern crate alloc;
use std::prelude::v1::*;
#[cfg(feature = "certificates")]
pub mod certificates;
#[cfg(feature = "certificates_custom")]
pub mod certificates_custom;
#[cfg(feature = "certificates_custom")]
mod x509_cert;
#[cfg(any(feature = "auth_verify", feature = "diffie_hellman_key_store", feature = "diffie_hellman_client_server_key_store"))]
use std::collections::BTreeMap;
#[cfg(any(feature = "hash", feature = "hasher", feature = "mac"))]
use blake2::digest::{ Mac, FixedOutput, Digest };
#[cfg(feature = "public_private_sign_verify")]
use ring::{
signature::KeyPair
};
#[cfg(feature = "password_hash")]
use argon2::{
password_hash::{ PasswordHasher, PasswordVerifier }
};
#[cfg(any(feature = "generator", feature = "symmetric_encrypt_decrypt"))]
pub use product_os_random::{ RandomGenerator, RandomGeneratorTemplate };
#[cfg(feature = "jwt_encrypt_decrypt")]
use base64::{ Engine as _, engine::general_purpose as base64GP };
#[cfg(feature = "jwt_auth_verify")]
use jwt_compact::{ prelude::*, alg::{ Hs256, Hs256Key } };
#[cfg(feature = "jwt_auth_verify")]
use serde::{Serialize, Deserialize};
#[cfg(feature = "default")]
use product_os_random::SeedableRng;
#[cfg(any(feature = "auth_verify", feature = "byte_vector"))]
pub trait AsByteVector {
fn as_byte_vector(&self) -> Vec<u8>;
}
#[cfg(any(feature = "auth_verify", feature = "byte_vector"))]
impl AsByteVector for serde_json::Value {
fn as_byte_vector(&self) -> Vec<u8> {
self.to_string().into_bytes()
}
}
#[cfg(feature = "auth_verify")]
pub fn create_auth_request<T>(query: Option<&BTreeMap<String, String>>, encoded: bool, payload: Option<T>,
headers: Option<&BTreeMap<String, String>>, exclude_headers: &[&str], key: Option<&[u8]>) -> String
where T: AsByteVector
{
let mut representation: Vec<u8> = vec!();
match query {
Some(q) => representation.extend_from_slice(create_auth_query(q, encoded, key).as_bytes()),
None => ()
}
match headers {
Some(h) => representation.extend_from_slice(create_auth_headers(h, exclude_headers, key).as_bytes()),
None => ()
}
match payload {
Some(p) => representation.extend_from_slice(create_auth_payload(p, key).as_slice()),
None => ()
}
hex_encode(create_auth(representation.as_slice(), key, None))
}
#[cfg(feature = "auth_verify")]
pub fn verify_auth_request<T>(query: Option<&BTreeMap<String, String>>, encoded: bool, payload: Option<T>,
headers: Option<&BTreeMap<String, String>>, exclude_headers: &[&str], tag: String, key: Option<&[u8]>) -> bool
where T: AsByteVector
{
let auth = create_auth_request(query, encoded, payload, headers, exclude_headers, key);
auth == tag
}
#[cfg(feature = "auth_verify")]
pub fn create_auth_request_json(query: Option<&BTreeMap<String, String>>, encoded: bool, json: Option<&serde_json::Value>,
headers: Option<&BTreeMap<String, String>>, exclude_headers: &[&str], key: Option<&[u8]>) -> String
{
let mut representation: Vec<u8> = vec!();
match query {
Some(q) => representation.extend_from_slice(create_auth_query(q, encoded, key).as_bytes()),
None => ()
}
match headers {
Some(h) => representation.extend_from_slice(create_auth_headers(h, exclude_headers, key).as_bytes()),
None => ()
}
match json {
Some(v) => representation.extend_from_slice(create_auth_json(&v, key).as_slice()),
None => ()
}
hex_encode(create_auth(representation.as_slice(), key, None))
}
#[cfg(feature = "auth_verify")]
pub fn verify_auth_request_json(query: Option<&BTreeMap<String, String>>, encoded: bool, json: Option<&serde_json::Value>,
headers: Option<&BTreeMap<String, String>>, exclude_headers: &[&str], tag: String, key: Option<&[u8]>) -> bool
{
let auth = create_auth_request_json(query, encoded, json, headers, exclude_headers, key);
auth == tag
}
#[cfg(feature = "auth_verify")]
pub fn create_auth_query(query: &BTreeMap<String, String>, encoded: bool, key: Option<&[u8]>) -> String {
let mut representation: Vec<u8> = vec!();
for (key, value) in query {
if encoded {
representation.extend_from_slice(product_os_urlencoding::decode(key.as_str()).unwrap().to_string().as_bytes());
representation.extend_from_slice(product_os_urlencoding::decode(value.as_str()).unwrap().to_string().as_bytes());
}
else {
representation.extend_from_slice(key.as_bytes());
representation.extend_from_slice(value.as_bytes());
}
}
hex_encode(create_auth(representation.as_slice(), key, None))
}
#[cfg(feature = "auth_verify")]
pub fn verify_auth_query(query: &BTreeMap<String, String>, encoded: bool, tag: String, key: Option<&[u8]>) -> bool
{
let auth = create_auth_query(query, encoded, key);
auth == tag
}
#[cfg(feature = "auth_verify")]
pub fn create_auth_headers(headers: &BTreeMap<String, String>, exclude_headers: &[&str], key: Option<&[u8]>) -> String {
let mut representation: Vec<u8> = vec!();
for (key, value) in headers {
if !exclude_headers.contains(&key.as_str()) {
representation.extend_from_slice(key.as_bytes());
representation.extend_from_slice(value.as_bytes());
}
}
hex_encode(create_auth(representation.as_slice(), key, None))
}
#[cfg(feature = "auth_verify")]
pub fn verify_auth_headers(headers: &BTreeMap<String, String>, encoded: bool, tag: String, key: Option<&[u8]>) -> bool
{
let auth = create_auth_query(headers, encoded, key);
auth == tag
}
#[cfg(feature = "auth_verify")]
pub fn create_auth_payload<T>(payload: T, key: Option<&[u8]>) -> Vec<u8>
where T: AsByteVector
{
let byte_representation = payload.as_byte_vector();
create_auth(byte_representation.as_slice(), key, None)
}
#[cfg(feature = "auth_verify")]
pub fn verify_auth_payload<T>(payload: T, tag: &[u8], key: Option<&[u8]>) -> bool
where T: AsByteVector
{
let auth = create_auth_payload(payload, key);
verify_auth(auth, tag, key, None)
}
#[cfg(feature = "auth_verify")]
pub fn create_auth_json(json: &serde_json::Value, key: Option<&[u8]>) -> Vec<u8>
{
let byte_representation = json.to_string();
create_auth(byte_representation.as_bytes(), key, None)
}
#[cfg(feature = "auth_verify")]
pub fn verify_auth_json(json: &serde_json::Value, tag: &[u8], key: Option<&[u8]>) -> bool
{
let auth = create_auth_json(json, key);
verify_auth(auth, tag, key, None)
}
#[cfg(feature = "auth_verify")]
pub fn create_auth(data: &[u8], key: Option<&[u8]>, algorithm: Option<&str>) -> Vec<u8> {
match key {
Some(k) => {
if k.len() == 0 { return Vec::from([]); } match algorithm {
Some(alg) => {
match alg {
"blake2" => blake2_mac(data, k),
_ => blake2_mac(data, k)
}
},
None => blake2_mac(data, k)
}
},
None => {
match algorithm {
Some(alg) => {
match alg {
"blake2" => blake2_hash(data),
_ => blake2_hash(data)
}
},
None => blake2_hash(data)
}
}
}
}
#[cfg(feature = "auth_verify")]
pub fn verify_auth(data: Vec<u8>, tag: &[u8], key: Option<&[u8]>, algorithm: Option<&str>) -> bool {
match key {
Some(k) => {
if k.len() == 0 { return false; } match algorithm {
Some(alg) => {
match alg {
"blake2" => blake2_verify_mac(data, tag, k),
_ => blake2_verify_mac(data, tag, k)
}
},
None => blake2_verify_mac(data, tag, k)
}
},
None => {
match algorithm {
Some(alg) => {
match alg {
"blake2" => blake2_hash(data.as_slice()).as_slice() == tag,
_ => blake2_hash(data.as_slice()).as_slice() == tag
}
},
None => blake2_hash(data.as_slice()).as_slice() == tag
}
}
}
}
#[cfg(feature = "hash")]
pub fn create_salted_hash(data: &[u8], generator: &mut RandomGenerator, algorithm: Option<&str>) -> (Vec<u8>, Vec<u8>) {
let salt = generator.get_random_string(16).as_bytes().to_vec();
let mut input = data.to_vec();
input.append(&mut salt.to_vec());
match algorithm {
Some(alg) => {
match alg {
"paranoid_hash" => (create_string_hash(std::str::from_utf8(input.as_slice()).unwrap().to_string()).into_bytes(), salt),
"blake2" => (blake2_hash(input.as_slice()), salt),
_ => (blake2_hash(input.as_slice()), salt)
}
},
None => (blake2_hash(input.as_slice()), salt)
}
}
#[cfg(feature = "hash")]
pub fn create_hash(data: &[u8], algorithm: Option<&str>) -> Vec<u8> {
match algorithm {
Some(alg) => {
match alg {
"paranoid_hash" => create_string_hash(std::str::from_utf8(data).unwrap().to_string()).into_bytes(),
"blake2" => blake2_hash(data),
_ => blake2_hash(data)
}
},
None => blake2_hash(data)
}
}
#[cfg(feature = "hash")]
pub fn verify_hash(data: &[u8], original_hash: &[u8], stored_salt: Option<&[u8]>, algorithm: Option<&str>) -> bool {
let mut input = data.to_vec();
match stored_salt {
None => (),
Some(salt) => input.append(&mut salt.to_vec())
}
let hash = create_hash(input.as_slice(), algorithm);
if hash.as_slice() == original_hash { true }
else { false }
}
#[cfg(feature = "auth_verify")]
pub fn hex_encode(data: Vec<u8>) -> String {
hex::encode(data)
}
#[cfg(feature = "auth_verify")]
pub fn hex_decode(value: String) -> Vec<u8> {
hex::decode(value).unwrap()
}
#[cfg(feature = "jwt_encrypt_decrypt")]
pub fn base64_encode(data: Vec<u8>) -> String {
base64GP::URL_SAFE_NO_PAD.encode(data)
}
#[cfg(feature = "jwt_encrypt_decrypt")]
pub fn base64_decode(value: String) -> Vec<u8> {
match base64GP::URL_SAFE_NO_PAD.decode(value) {
Ok(output) => output,
Err(_) => vec![]
}
}
#[cfg(feature = "hash")]
pub fn create_string_hash(data: String) -> String {
let mut hasher = blake2::Blake2b512::new();
hasher.update(data.as_bytes());
let hash = hasher.finalize();
String::from_utf8_lossy(&hash[..]).to_string()
}
#[cfg(feature = "hash")]
fn blake2_hash(data: &[u8]) -> Vec<u8> {
let mut hasher = blake2::Blake2b512::new();
hasher.update(data);
let hash = hasher.finalize();
hash[..].to_vec()
}
#[cfg(feature = "hasher")]
#[derive(Clone)]
pub struct ProductOSHasher {
hasher: blake2::Blake2b<blake2::digest::typenum::U8>
}
#[cfg(feature = "hasher")]
impl ProductOSHasher {
pub fn new() -> Self {
Self {
hasher: blake2::Blake2b::default()
}
}
}
#[cfg(feature = "hasher")]
impl std::hash::Hasher for ProductOSHasher {
fn finish(&self) -> u64 {
u64::from_be_bytes(self.hasher.clone().finalize().into())
}
fn write(&mut self, bytes: &[u8]) {
self.hasher.update(bytes)
}
}
#[cfg(feature = "hasher")]
impl std::hash::BuildHasher for ProductOSHasher {
type Hasher = ProductOSHasher;
fn build_hasher(&self) -> Self::Hasher {
ProductOSHasher::default()
}
}
#[cfg(feature = "hasher")]
impl Default for ProductOSHasher {
fn default() -> Self {
ProductOSHasher::new()
}
}
#[cfg(feature = "mac")]
fn blake2_mac(data: &[u8], key: &[u8]) -> Vec<u8> {
match blake2::Blake2bMac512::new_from_slice(key) {
Ok(mut mac) => {
mac.update(data);
let hash = mac.finalize_fixed();
hash.to_vec()
},
Err(e) => panic!("Invalid key length provided to blake2 auth: {}", e)
}
}
#[cfg(feature = "mac")]
fn blake2_verify_mac(msg: Vec<u8>, tag: &[u8], key: &[u8]) -> bool {
let auth = blake2_mac(msg.as_slice(), key);
auth == tag
}
#[cfg(feature = "jwt_auth_verify")]
pub struct JWTGenerator {
issuer: String,
default_until: i64,
default_audience: String,
jti_length: usize,
random_generator: RandomGenerator,
time_generator: product_os_async_executor::moment::Moment
}
#[cfg(feature = "jwt_auth_verify")]
pub trait TokenClaims<'a, T>: Clone
{
fn verify(&self, verify_map: T) -> bool;
}
#[cfg(feature = "jwt_auth_verify")]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct DefaultClaims {
#[serde(rename = "iss")]
issuer: String,
#[serde(rename = "sub")]
subject: String,
#[serde(rename = "aud")]
audience: String,
#[serde(rename = "jti")]
jwt_id: String
}
#[cfg(feature = "jwt_auth_verify")]
impl TokenClaims<'_, DefaultClaims> for DefaultClaims {
fn verify(&self, verify_map: DefaultClaims) -> bool {
self.issuer == verify_map.issuer && self.subject == verify_map.subject &&
self.audience == verify_map.audience && self.jwt_id == verify_map.jwt_id
}
}
#[cfg(feature = "jwt_auth_verify")]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct EmptyClaims {}
impl EmptyClaims {
pub fn new() -> Self {
EmptyClaims {}
}
}
#[cfg(feature = "jwt_auth_verify")]
impl TokenClaims<'_, EmptyClaims> for EmptyClaims {
fn verify(&self, verify_map: EmptyClaims) -> bool {
true
}
}
#[cfg(feature = "jwt_auth_verify")]
pub enum JWTError {
InvalidSignature,
InvalidKey,
EncodeFailure,
DecodeFailure,
InvalidClaims,
InvalidNonce
}
#[cfg(feature = "jwt_auth_verify")]
impl JWTGenerator where {
pub fn new(gen: Option<product_os_random::RNG>, func: Option<fn() -> chrono::DateTime<chrono::Utc>>, issuer: String, default_until: i64, default_audience: String, jti_length: usize) -> Self {
let time_gen = product_os_async_executor::moment::Moment::new(func);
Self {
issuer,
default_until,
default_audience,
jti_length,
random_generator: RandomGenerator::new(gen),
time_generator: time_gen
}
}
pub fn get_default_until(&self) -> &i64 {
&self.default_until
}
pub fn get_default_audience(&self) -> String {
self.default_audience.to_owned()
}
pub fn generate_default_claims(&self, subject: String, audience: Option<String>, jwt_id: String) -> DefaultClaims {
let audience = match audience {
Some(a) => a,
None => self.default_audience.to_owned()
};
DefaultClaims {
issuer: self.issuer.to_owned(),
subject,
audience,
jwt_id
}
}
pub fn jwt_auth<'a, T: TokenClaims<'a, T> + Serialize>(&mut self, subject: String, audience: Option<String>, until: Option<chrono::DateTime<chrono::Utc>>, custom_claims: Option<T>, custom_header: Option<Header>, jwt_secret: &[u8], encryption_key: Option<Vec<u8>>, gen: &mut Option<impl product_os_random::RngCore>) -> Result<(String, String), JWTError> {
let header = match custom_header {
None => jwt_compact::Header::empty(),
Some(h) => h
};
let now = self.time_generator.now();
let time_func = self.time_generator.get_function();
let time_options = jwt_compact::TimeOptions::new(chrono::Duration::seconds(5), time_func);
let until = match until {
None => chrono::Duration::seconds(self.default_until.to_owned()),
Some(u) => chrono::Duration::seconds(u.timestamp() - now.timestamp())
};
match encryption_key {
None => {
let jti = self.random_generator.get_random_string(self.jti_length.into());
match custom_claims {
None => {
let claims = jwt_compact::Claims::new(self.generate_default_claims(subject, audience,jti.to_owned()))
.set_duration_and_issuance(&time_options, until)
.set_not_before(now);
let key = Hs256Key::new(jwt_secret);
match Hs256.token(&header, &claims, &key) {
Ok(jwt) => Ok((jwt, jti)),
Err(_) => Err(JWTError::EncodeFailure)
}
},
Some(custom) => {
let claims = jwt_compact::Claims::new(custom)
.set_duration_and_issuance(&time_options, until)
.set_not_before(now);
let key = Hs256Key::new(jwt_secret);
match Hs256.token(&header, &claims, &key) {
Ok(jwt) => Ok((jwt, jti)),
Err(_) => Err(JWTError::EncodeFailure)
}
}
}
},
Some(enc_secret) => {
#[cfg(feature = "jwt_encrypt_decrypt")]
{
let nonce = {
#[cfg(feature = "jwt_encrypt_decrypt_std")] {
orion::hazardous::stream::xchacha20::Nonce::generate()
}
#[cfg(not(feature = "jwt_encrypt_decrypt_std"))] {
let bytes = product_os_random::RandomGenerator::get_random_bytes_one_time(24, gen);
orion::hazardous::aead::xchacha20poly1305::Nonce::from_slice(bytes.as_slice()).unwrap()
}
};
let jti = base64GP::URL_SAFE_NO_PAD.encode(nonce.to_owned());
match custom_claims {
None => {
let claims = jwt_compact::Claims::new(self.generate_default_claims(subject, audience, jti.to_owned()))
.set_duration_and_issuance(&time_options, until)
.set_not_before(now);
let key = Hs256Key::new(jwt_secret);
match Hs256.token(&header, &claims, &key) {
Ok(jwt) => {
match orion::hazardous::aead::xchacha20poly1305::SecretKey::from_slice(enc_secret.as_slice()) {
Ok(secret) => {
let mut output = vec![];
match orion::hazardous::aead::xchacha20poly1305::seal(&secret, &nonce, jwt.as_bytes(), None, &mut output) {
Ok(_) => Ok((base64GP::URL_SAFE_NO_PAD.encode(output), jti)),
Err(e) => panic!("Error encrypting value {}", e)
}
},
Err(e) => panic!("Invalid key {}", e)
}
}
Err(_) => Err(JWTError::EncodeFailure)
}
},
Some(custom) => {
let claims = jwt_compact::Claims::new(custom)
.set_duration_and_issuance(&time_options, until)
.set_not_before(now);
let key = Hs256Key::new(jwt_secret);
match Hs256.token(&header, &claims, &key) {
Ok(jwt) => {
match orion::hazardous::aead::xchacha20poly1305::SecretKey::from_slice(enc_secret.as_slice()) {
Ok(secret) => {
let mut output = vec![];
match orion::hazardous::aead::xchacha20poly1305::seal(&secret, &nonce, jwt.as_bytes(), None, &mut output) {
Ok(_) => Ok((base64GP::URL_SAFE_NO_PAD.encode(output), jti)),
Err(e) => panic!("Error encrypting value {}", e)
}
},
Err(e) => panic!("Invalid key {}", e)
}
}
Err(_) => Err(JWTError::EncodeFailure)
}
}
}
}
#[cfg(not(feature = "jwt_encrypt_decrypt"))]
{
panic!("JWT Encryption not enabled - add feature jwt_encrypt_decrypt to product_os_security");
}
}
}
}
#[cfg(feature = "jwt_auth_verify")]
pub fn jwt_get_token_claims<'a, T: TokenClaims<'a, T> + serde::de::DeserializeOwned>(&self, token: String, jwt_secret: &[u8], nonce: &[u8], decryption_key: Option<Vec<u8>>) -> Result<(String, Token<T>), JWTError>
{
let token_string = match decryption_key {
None => token,
Some(dec_secret) => {
#[cfg(feature = "jwt_encrypt_decrypt")]
{
match base64GP::URL_SAFE_NO_PAD.decode(token) {
Ok(decoded) => {
match orion::hazardous::aead::xchacha20poly1305::SecretKey::from_slice(dec_secret.as_slice()) {
Ok(secret) => {
match orion::hazardous::aead::xchacha20poly1305::Nonce::from_slice(nonce) {
Ok(nonce) => {
let mut output = vec![];
match orion::hazardous::aead::xchacha20poly1305::open(&secret, &nonce, decoded.as_slice(), None, &mut output) {
Ok(_) => match String::from_utf8(output) {
Ok(v) => v,
Err(_) => return Err(JWTError::InvalidSignature)
},
Err(_) => return Err(JWTError::InvalidSignature)
}
}
Err(_) => return Err(JWTError::InvalidNonce)
}
},
Err(_) => return Err(JWTError::InvalidKey)
}
}
Err(_) => return Err(JWTError::InvalidSignature)
}
}
#[cfg(not(feature = "jwt_encrypt_decrypt"))]
{
panic!("JWT Decryption not enabled - add feature jwt_encrypt_decrypt to product_os_security");
}
}
};
let token = match jwt_compact::UntrustedToken::new(&token_string) {
Ok(ut) => ut,
Err(e) => return Err(JWTError::DecodeFailure)
};
let key = Hs256Key::new(jwt_secret);
let token_claims: Token<T> = match Hs256.validator(&key).validate(&token) {
Ok(claims) => claims,
Err(_) => return Err(JWTError::InvalidSignature)
};
Ok((token_string, token_claims))
}
#[cfg(feature = "jwt_auth_verify")]
pub fn jwt_verify_auth<'a, T: TokenClaims<'a, T> + serde::de::DeserializeOwned>(&self, verify_claims: T, token: String, jwt_secret: &[u8], nonce: &[u8], decryption_key: Option<Vec<u8>>) -> Result<(String, T), JWTError> {
match self.jwt_get_token_claims::<T>(token, jwt_secret, nonce, decryption_key) {
Ok((token_string, token_claims)) => {
let time_func = self.time_generator.get_function();
let time_options = jwt_compact::TimeOptions::new(chrono::Duration::seconds(5), time_func);
let claims = token_claims.claims();
match claims.validate_expiration(&time_options) {
Ok(_) => {}
Err(_) => return Err(JWTError::InvalidClaims)
}
match claims.validate_maturity(&time_options) {
Ok(_) => {}
Err(_) => return Err(JWTError::InvalidClaims)
}
let custom_claims: T = claims.custom.to_owned();
match custom_claims.verify(verify_claims) {
true => Ok((token_string, custom_claims)),
false => return Err(JWTError::InvalidClaims)
}
}
Err(e) => Err(e)
}
}
}
#[cfg(feature = "public_private_sign_verify")]
pub enum KeyError {
KeyRejected(String),
}
#[cfg(feature = "public_private_sign_verify")]
pub fn generate_public_private_keys() -> Result<ring::pkcs8::Document, ring::error::Unspecified> {
let rng = ring::rand::SystemRandom::new();
ring::signature::Ed25519KeyPair::generate_pkcs8(&rng)
}
#[cfg(feature = "public_private_sign_verify")]
pub fn get_public_key(key_pair: ring::pkcs8::Document) -> Result<Vec<u8>, KeyError> {
match ring::signature::Ed25519KeyPair::from_pkcs8(key_pair.as_ref()) {
Ok(key_pair) => {
Ok(key_pair.public_key().as_ref().to_vec())
},
Err(_) => Err(KeyError::KeyRejected(String::from("No public key found")))
}
}
#[cfg(feature = "public_private_sign_verify")]
pub fn private_key_sign(key_pair: ring::pkcs8::Document, message: &[u8]) -> Result<ring::signature::Signature, KeyError> {
match ring::signature::Ed25519KeyPair::from_pkcs8(key_pair.as_ref()) {
Ok(key_pair) => Ok(key_pair.sign(message)),
Err(_) => Err(KeyError::KeyRejected(String::from("Failed to sign with key")))
}
}
#[cfg(feature = "public_private_sign_verify")]
pub fn public_key_verify(public_key_bytes: &[u8], signature: &[u8], message: &[u8]) -> bool {
let peer_public_key = ring::signature::UnparsedPublicKey::new(&ring::signature::ED25519, public_key_bytes);
match peer_public_key.verify(message, signature) {
Ok(_) => true,
Err(_) => false
}
}
#[cfg(feature = "symmetric_encrypt_decrypt")]
pub enum CryptoError {
Unknown(String),
}
#[cfg(feature = "symmetric_encrypt_decrypt")]
pub fn symmetric_encrypt(key: &[u8], message: &[u8], gen: &mut Option<impl product_os_random::RngCore>) -> Result<(Vec<u8>, Vec<u8>), CryptoError> {
match orion::hazardous::aead::xchacha20poly1305::SecretKey::from_slice(key) {
Ok(secret) => {
let nonce = {
#[cfg(feature = "jwt_encrypt_decrypt_std")] {
orion::hazardous::stream::xchacha20::Nonce::generate()
}
#[cfg(not(feature = "jwt_encrypt_decrypt_std"))] {
let bytes = product_os_random::RandomGenerator::get_random_bytes_one_time(24, gen);
orion::hazardous::aead::xchacha20poly1305::Nonce::from_slice(bytes.as_slice()).unwrap()
}
};
let mut output = vec![];
match orion::hazardous::aead::xchacha20poly1305::seal(&secret, &nonce, message, None, &mut output) {
Ok(_) => Ok((output, nonce.as_ref().to_vec())),
Err(e) => Err(CryptoError::Unknown(e.to_string()))
}
},
Err(e) => panic!("Invalid key {}", e)
}
}
#[cfg(feature = "symmetric_encrypt_decrypt")]
pub fn symmetric_decrypt(key: &[u8], nonce: &[u8], cipher: &[u8]) -> Result<Vec<u8>, CryptoError> {
match orion::hazardous::aead::xchacha20poly1305::SecretKey::from_slice(key) {
Ok(secret) => {
match orion::hazardous::aead::xchacha20poly1305::Nonce::from_slice(nonce) {
Ok(nonce) => {
let mut output = vec![];
match orion::hazardous::aead::xchacha20poly1305::open(&secret, &nonce,cipher, None, &mut output) {
Ok(_) => Ok(output),
Err(e) => Err(CryptoError::Unknown(e.to_string()))
}
}
Err(e) => Err(CryptoError::Unknown(e.to_string()))
}
},
Err(e) => panic!("Invalid key {}", e)
}
}
#[cfg(feature = "public_private_encrypt_decrypt")]
pub struct RSA {
bit_count: usize,
private_key: Vec<u8>,
public_key: Vec<u8>,
rng: product_os_random::ThreadRng
}
#[cfg(feature = "public_private_encrypt_decrypt")]
impl RSA {
pub fn new() -> Self {
let bit_count = 2048;
let rng = product_os_random::thread_rng();
let private_key = vec!();
let public_key = vec!();
Self {
bit_count,
private_key,
public_key,
rng,
}
}
fn generate_keys(&self) -> (Vec<u8>, Vec<u8>) {
let private_key = vec!();
let public_key = vec!();
(private_key, public_key)
}
pub fn generate_new_keys(&mut self) -> Vec<u8> {
todo!()
}
pub fn get_public_key(&self) -> Vec<u8> {
todo!()
}
pub fn import_public_key(&mut self, public_key: &[u8]) {
todo!()
}
pub fn self_encrypt(&self, data: &[u8]) -> Vec<u8> {
todo!()
}
pub fn encrypt(public_key: &[u8], data: &[u8]) -> Vec<u8> {
todo!()
}
pub fn decrypt(&self, cipher: &[u8]) -> Vec<u8> {
todo!()
}
}
#[cfg(feature = "diffie_hellman_key_store")]
pub struct DHKeyStore {
keys: BTreeMap<String, [u8; 32]>,
sessions: BTreeMap<String, x25519_dalek::EphemeralSecret>,
}
#[cfg(feature = "diffie_hellman_key_store")]
impl DHKeyStore {
pub fn new() -> Self {
let keys = BTreeMap::new();
let sessions = BTreeMap::new();
Self {
keys,
sessions
}
}
pub fn create_session(&mut self, #[cfg(not(feature = "default"))] crypto_rng: impl product_os_random::RngCrypto + 'static) -> (String, [u8; 32]) {
let identifier = uuid::Uuid::new_v4().to_string();
let secret =
{
#[cfg(feature = "default")]
{
x25519_dalek::EphemeralSecret::random_from_rng(product_os_random::CryptoRNG::Std(product_os_random::StdRng::from_entropy()))
}
#[cfg(not(feature = "default"))]
{
x25519_dalek::EphemeralSecret::random_from_rng(product_os_random::CustomCryptoRng::new(crypto_rng))
}
};
let key = x25519_dalek::PublicKey::from(&secret);
self.sessions.insert(identifier.clone(), secret);
(identifier, key.to_bytes())
}
pub fn generate_key(&mut self, session_identifier: String, remote_public_key: &[u8], association: String, remote_session_identifier: Option<String>) {
let identifier = session_identifier.as_str();
if self.sessions.contains_key(identifier) {
match self.sessions.remove(session_identifier.as_str()) {
Some(session) => {
let remote_public_key_bytes: [u8; 32] = match remote_public_key.try_into() {
Ok(rpkb) => rpkb,
Err(_) => [0; 32]
};
let remote_key = x25519_dalek::PublicKey::from(remote_public_key_bytes);
let key= session.diffie_hellman(&remote_key);
let ikm = key.as_bytes();
let salt = None;
let info = match remote_session_identifier {
None => session_identifier.into_bytes(),
Some(rsi) => rsi.into_bytes()
};
let mut output_key = [0u8; 32];
let key_derive = hkdf::Hkdf::<sha2::Sha512>::new(salt, ikm);
match key_derive.expand(info.as_slice(), &mut output_key) {
Ok(_) => self.keys.insert(association, output_key),
Err(e) => panic!("{}", e)
};
},
None => ()
}
}
}
pub fn get_key(&self, association: String) -> Option<&[u8]> {
match self.keys.get(association.as_str()) {
Some(keys) => {
Some(keys)
},
None => None
}
}
}
#[cfg(feature = "diffie_hellman_client_server_key_store")]
pub enum DHClientServerSessionKind {
Client,
Server
}
#[cfg(feature = "diffie_hellman_client_server_key_store")]
pub struct DHClientServerKeyStore {
keys: BTreeMap<String, orion::kex::SessionKeys>,
client_sessions: BTreeMap<String, orion::kex::EphemeralClientSession>,
server_sessions: BTreeMap<String, orion::kex::EphemeralServerSession>
}
#[cfg(feature = "diffie_hellman_client_server_key_store")]
impl DHClientServerKeyStore {
pub fn new() -> Self {
let keys = BTreeMap::new();
let client_sessions = BTreeMap::new();
let server_sessions = BTreeMap::new();
Self {
keys,
client_sessions,
server_sessions
}
}
pub fn create_session(&mut self, kind: DHClientServerSessionKind) -> (String, [u8; 32]) {
let identifier = uuid::Uuid::new_v4().to_string();
match kind {
DHClientServerSessionKind::Client => {
let session = orion::kex::EphemeralClientSession::new().unwrap();
let key = session.public_key().clone();
self.client_sessions.insert(identifier.clone(), session);
(identifier, key.to_bytes())
}
DHClientServerSessionKind::Server => {
let session = orion::kex::EphemeralServerSession::new().unwrap();
let key = session.public_key().clone();
self.server_sessions.insert(identifier.clone(), session);
(identifier, key.to_bytes())
}
}
}
pub fn generate_keys(&mut self, session_identifier: String, remote_public_key: &[u8], association: String) {
let identifier = session_identifier.as_str();
if self.client_sessions.contains_key(identifier) {
match self.client_sessions.remove(session_identifier.as_str()) {
Some(session) => {
let remote_key = orion::kex::PublicKey::from_slice(remote_public_key).unwrap();
match session.establish_with_server(&remote_key) {
Ok(keys) => {
self.keys.insert(association, keys);
},
Err(_) => ()
}
},
None => ()
}
}
else if self.server_sessions.contains_key(identifier) {
match self.server_sessions.remove(session_identifier.as_str()) {
Some(session) => {
let remote_key = orion::kex::PublicKey::from_slice(remote_public_key).unwrap();
match session.establish_with_client(&remote_key) {
Ok(keys) => {
self.keys.insert(association, keys);
},
Err(_) => ()
}
},
None => ()
}
}
}
pub fn get_receiving_key(&self, association: String) -> Option<&[u8]> {
match self.keys.get(association.as_str()) {
Some(keys) => {
Some(keys.receiving().unprotected_as_bytes())
},
None => None
}
}
pub fn get_sending_key(&self, association: String) -> Option<&[u8]> {
match self.keys.get(association.as_str()) {
Some(keys) => {
Some(keys.transport().unprotected_as_bytes())
},
None => None
}
}
}
#[cfg(feature = "time_otp")]
struct TimeOTP {
step: u64,
digits: u32,
time_generator: product_os_async_executor::moment::Moment
}
#[cfg(feature = "time_otp")]
impl TimeOTP {
pub fn new(step: Option<u16>, digits: Option<u8>, func: Option<fn() -> chrono::DateTime<chrono::Utc>>) -> Self {
Self {
step: match step {
None => 30,
Some(s) => match u64::try_from(s) {
Ok(s64) => s64,
Err(_) => 0
}
},
digits: match digits {
None => 6,
Some(d) => match u32::try_from(d) {
Ok(d32) => d32,
Err(_) => 0
}
},
time_generator: product_os_async_executor::moment::Moment::new(func)
}
}
pub fn generate_time_otp(&self, secret: &[u8]) -> String {
let now = self.time_generator.unwrap().now();
let seconds = now.timestamp().unsigned_abs();
TimeOTP::totp_custom(self.step.to_owned(), self.digits.to_owned(), secret, seconds)
}
pub fn verify_time_otp(&self, secret: &[u8], code: String) -> bool {
let now = self.time_generator.unwrap().now();
let seconds = now.timestamp().unsigned_abs();
code == TimeOTP::totp_custom(self.step.to_owned(), self.digits.to_owned(), secret, seconds)
}
pub const DEFAULT_STEP: u64 = 30;
pub const DEFAULT_DIGITS: u32 = 8;
pub fn totp(secret: &[u8], time: u64) -> String {
TimeOTP::totp_custom(TimeOTP::DEFAULT_STEP, TimeOTP::DEFAULT_DIGITS, secret, time)
}
pub fn totp_custom(step: u64, digits: u32, secret: &[u8], time: u64) -> String {
let mac = blake2_mac(&(time / step).to_be_bytes(), secret);
let hash = mac.as_slice();
let offset: usize = (hash.last().unwrap() & 0xf) as usize;
let binary: u64 = (((hash[offset].to_owned() & 0x7f) as u64) << 24)
| ((hash[offset.to_owned() + 1].to_owned() as u64) << 16)
| ((hash[offset.to_owned() + 2].to_owned() as u64) << 8)
| (hash[offset.to_owned() + 3].to_owned() as u64);
format!("{:01$}", binary % (10_u64.pow(digits)), digits as usize)
}
fn to_bytes(n: u64) -> [u8; 8] {
let mask = 0x00000000000000ff;
let mut bytes: [u8; 8] = [0; 8];
(0..8).for_each(|i| bytes[7 - i] = (mask & (n >> (i.to_owned() * 8))) as u8);
bytes
}
}
#[cfg(feature = "password_hash")]
pub fn password_hash(password: &[u8], #[cfg(not(feature = "default"))] crypto_rng: impl product_os_random::RngCrypto + 'static) -> Result<Vec<u8>, argon2::password_hash::errors::Error> {
let argon2 = argon2::Argon2::new(argon2::Algorithm::Argon2id, argon2::Version::V0x13, argon2::Params::new(15000, 2, 1, None).unwrap());
let salt = {
#[cfg(feature = "default")]
{
argon2::password_hash::SaltString::generate(product_os_random::CryptoRNG::Std(product_os_random::StdRng::from_entropy()))
}
#[cfg(not(feature = "default"))]
{
argon2::password_hash::SaltString::generate(product_os_random::CustomCryptoRng::new(crypto_rng))
}
};
let password_hash = match argon2.hash_password(password, &salt) {
Ok(res) => res,
Err(e) => return Err(e)
};
Ok(password_hash.to_string().as_bytes().to_vec())
}
#[cfg(feature = "password_hash")]
pub fn password_verify(hash: &[u8], input: &[u8]) -> bool {
let hash_str = String::from_utf8_lossy(hash);
match argon2::PasswordHash::new(hash_str.as_ref()) {
Ok(password_hash) => {
match argon2::Argon2::default().verify_password( input, &password_hash) {
Ok(_) => true,
Err(_) => false
}
}
Err(_) => false
}
}
#[cfg(feature = "string_safe")]
pub fn encode_uri_component(value: String) -> String {
product_os_urlencoding::encode(value.as_str()).to_string()
}
#[cfg(feature = "string_safe")]
pub fn decode_uri_component(value: String) -> String {
product_os_urlencoding::decode(value.as_str()).unwrap().to_string()
}