Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 0 additions & 66 deletions .github/workflows/owasp-dc.yml

This file was deleted.

2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cas-lib"
version = "0.2.78"
version = "0.2.79"
edition = "2021"
description = "A function wrapper layer for RustCrypto and Dalek-Cryptography. Intended to be used in FFI situations with a global heap deallactor at the top level project."
license = "Apache-2.0"
Expand Down
123 changes: 65 additions & 58 deletions src/asymmetric/cas_rsa.rs
Original file line number Diff line number Diff line change
@@ -1,58 +1,65 @@
use rand::rngs::OsRng;
use rsa::{
pkcs1::{DecodeRsaPublicKey, EncodeRsaPublicKey},
pkcs8::{DecodePrivateKey, EncodePrivateKey},
RsaPublicKey,
};
use rsa::{Pkcs1v15Sign, RsaPrivateKey};

use super::types::{CASRSAEncryption, RSAKeyPairResult};

pub struct CASRSA;

impl CASRSAEncryption for CASRSA {
/// Generates an RSA key pair of the specified size.
/// The key size must be at of a supported kind like 1024, 2048, 4096 bits.
fn generate_rsa_keys(key_size: usize) -> RSAKeyPairResult {
// TODO: do a check for key_size, if it is too small, return an error
let mut rng: OsRng = OsRng;
let private_key: RsaPrivateKey =
RsaPrivateKey::new(&mut rng, key_size).expect("failed to generate a key");
let public_key: RsaPublicKey = private_key.to_public_key();
let result = RSAKeyPairResult {
public_key: public_key
.to_pkcs1_pem(rsa::pkcs1::LineEnding::LF)
.unwrap()
.to_string(),
private_key: private_key
.to_pkcs8_pem(rsa::pkcs8::LineEnding::LF)
.unwrap()
.to_string(),
};
result
}

/// Sign the given hash with the provided private key of the RSA key pair.
/// The parameter `hash` doesn't necessarily have to be a hash, it can be any data that you want to sign.
fn sign(private_key: String, hash: Vec<u8>) -> Vec<u8> {
let private_key = RsaPrivateKey::from_pkcs8_pem(&private_key).unwrap();
let signed_data = private_key
.sign(Pkcs1v15Sign::new_unprefixed(), &hash)
.unwrap();
signed_data
}


/// Verify the signature of the given hash with the provided public key of the RSA key pair.
/// The parameter `hash` doesn't necessarily have to be a hash, it can be any data that you want to verify.
/// Returns true if the signature is valid, false otherwise.
fn verify(public_key: String, hash: Vec<u8>, signature: Vec<u8>) -> bool {
let public_key = RsaPublicKey::from_pkcs1_pem(&public_key).unwrap();
let verified = public_key.verify(Pkcs1v15Sign::new_unprefixed(), &hash, &signature);
if verified.is_err() == false {
return true;
} else {
return false;
}
}
}
use rand::rngs::OsRng;
use rsa::{
pkcs1::{DecodeRsaPublicKey, EncodeRsaPublicKey},
pkcs8::{DecodePrivateKey, EncodePrivateKey},
RsaPublicKey,
};
use rsa::{Pkcs1v15Sign, RsaPrivateKey};

use crate::error::{CasError, CasResult};

use super::types::{CASRSAEncryption, RSAKeyPairResult};

/// The smallest RSA modulus size (in bits) this library will generate.
/// Keys below 2048 bits are considered insecure.
const MIN_RSA_KEY_SIZE: usize = 2048;

pub struct CASRSA;

impl CASRSAEncryption for CASRSA {
/// Generates an RSA key pair of the specified size.
/// The key size must be at least 2048 bits; smaller sizes are rejected with
/// [`CasError::InvalidParameters`].
fn generate_rsa_keys(key_size: usize) -> CasResult<RSAKeyPairResult> {
if key_size < MIN_RSA_KEY_SIZE {
return Err(CasError::InvalidParameters);
}
let mut rng: OsRng = OsRng;
let private_key: RsaPrivateKey =
RsaPrivateKey::new(&mut rng, key_size).map_err(|_| CasError::KeyGenerationFailed)?;
let public_key: RsaPublicKey = private_key.to_public_key();
Ok(RSAKeyPairResult {
public_key: public_key
.to_pkcs1_pem(rsa::pkcs1::LineEnding::LF)
.map_err(|_| CasError::InvalidPemKey)?
.to_string(),
private_key: private_key
.to_pkcs8_pem(rsa::pkcs8::LineEnding::LF)
.map_err(|_| CasError::InvalidPemKey)?
.to_string(),
})
}

/// Sign the given hash with the provided private key of the RSA key pair.
/// The parameter `hash` doesn't necessarily have to be a hash, it can be any data that you want to sign.
fn sign(private_key: String, hash: Vec<u8>) -> CasResult<Vec<u8>> {
let private_key =
RsaPrivateKey::from_pkcs8_pem(&private_key).map_err(|_| CasError::InvalidPemKey)?;
private_key
.sign(Pkcs1v15Sign::new_unprefixed(), &hash)
.map_err(|_| CasError::SigningFailed)
}


/// Verify the signature of the given hash with the provided public key of the RSA key pair.
/// The parameter `hash` doesn't necessarily have to be a hash, it can be any data that you want to verify.
/// Returns `Ok(true)` if the signature is valid, `Ok(false)` if it is not, and an
/// error if the public key could not be parsed.
fn verify(public_key: String, hash: Vec<u8>, signature: Vec<u8>) -> CasResult<bool> {
let public_key =
RsaPublicKey::from_pkcs1_pem(&public_key).map_err(|_| CasError::InvalidPemKey)?;
Ok(public_key
.verify(Pkcs1v15Sign::new_unprefixed(), &hash, &signature)
.is_ok())
}
}
10 changes: 6 additions & 4 deletions src/asymmetric/types.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use crate::error::CasResult;

pub struct RSAKeyPairResult {
pub private_key: String,
pub public_key: String,
}

pub trait CASRSAEncryption {
fn generate_rsa_keys(key_size: usize) -> RSAKeyPairResult;
fn sign(private_key: String, hash: Vec<u8>) -> Vec<u8>;
fn verify(public_key: String, hash: Vec<u8>, signed_text: Vec<u8>) -> bool;
}
fn generate_rsa_keys(key_size: usize) -> CasResult<RSAKeyPairResult>;
fn sign(private_key: String, hash: Vec<u8>) -> CasResult<Vec<u8>>;
fn verify(public_key: String, hash: Vec<u8>, signed_text: Vec<u8>) -> CasResult<bool>;
}
18 changes: 11 additions & 7 deletions src/compression/zstd.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
use std::io::Cursor;

use crate::error::{CasError, CasResult};

/// Compresses data using Zstandard compression algorithm.
/// The `level` parameter controls the compression level (0-22).
/// Higher levels result in better compression but slower performance.
pub fn compress(data_to_compress: Vec<u8>, level: i32) -> Vec<u8> {
pub fn compress(data_to_compress: Vec<u8>, level: i32) -> CasResult<Vec<u8>> {
let cursor = Cursor::new(data_to_compress);
let mut compressed_data = Vec::new();
zstd::stream::copy_encode(cursor, &mut compressed_data, level).unwrap();
compressed_data
zstd::stream::copy_encode(cursor, &mut compressed_data, level)
.map_err(|_| CasError::CompressionFailed)?;
Ok(compressed_data)
}

/// Decompresses data using Zstandard decompression algorithm.
pub fn decompress(data_to_decompress: Vec<u8>) -> Vec<u8> {
pub fn decompress(data_to_decompress: Vec<u8>) -> CasResult<Vec<u8>> {
let mut cursor = Cursor::new(data_to_decompress);
let mut decompressed_data = Vec::new();
zstd::stream::copy_decode(&mut cursor, &mut decompressed_data).unwrap();
decompressed_data
}
zstd::stream::copy_decode(&mut cursor, &mut decompressed_data)
.map_err(|_| CasError::CompressionFailed)?;
Ok(decompressed_data)
}
62 changes: 62 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use std::fmt;

/// The unified error type returned by all fallible `cas-lib` operations.
///
/// Every cryptographic operation that can fail returns [`CasResult`] instead of
/// panicking. This is important for FFI consumers: a panic unwinding across the
/// FFI boundary is undefined behavior and typically aborts the host process. A
/// malformed key, a tampered ciphertext, or a failed authentication tag are all
/// recoverable conditions and are reported through this enum.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CasError {
/// A provided key had an invalid length or could not be parsed.
InvalidKey,
/// A provided nonce/IV had an invalid length.
InvalidNonce,
/// A provided signature had an invalid length or could not be parsed.
InvalidSignature,
/// Input bytes could not be decoded into the expected type.
InvalidInput,
/// PEM/DER decoding or encoding of a key failed.
InvalidPemKey,
/// Invalid algorithm parameters were supplied (e.g. an RSA key size that is
/// too small, or out-of-range password-hashing parameters).
InvalidParameters,
/// AEAD encryption failed.
EncryptionFailed,
/// AEAD decryption failed or the authentication tag did not verify.
DecryptionFailed,
/// A signing operation failed.
SigningFailed,
/// Key generation failed.
KeyGenerationFailed,
/// Password hashing or verification setup failed.
PasswordHashingFailed,
/// Compression or decompression failed.
CompressionFailed,
}

impl fmt::Display for CasError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let message = match self {
CasError::InvalidKey => "invalid key: wrong length or could not be parsed",
CasError::InvalidNonce => "invalid nonce: wrong length",
CasError::InvalidSignature => "invalid signature: wrong length or could not be parsed",
CasError::InvalidInput => "invalid input: could not be decoded",
CasError::InvalidPemKey => "invalid PEM key: could not be decoded or encoded",
CasError::InvalidParameters => "invalid algorithm parameters",
CasError::EncryptionFailed => "encryption failed",
CasError::DecryptionFailed => "decryption failed or authentication tag mismatch",
CasError::SigningFailed => "signing failed",
CasError::KeyGenerationFailed => "key generation failed",
CasError::PasswordHashingFailed => "password hashing failed",
CasError::CompressionFailed => "compression or decompression failed",
};
f.write_str(message)
}
}

impl std::error::Error for CasError {}

/// The result type returned by all fallible `cas-lib` operations.
pub type CasResult<T> = Result<T, CasError>;
8 changes: 5 additions & 3 deletions src/hybrid/cas_hybrid.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::error::CasResult;

pub trait CASHybrid {
fn generate_key_pair() -> (Vec<u8>, Vec<u8>, Vec<u8>);
fn generate_info_str() -> Vec<u8>;
fn encrypt(plaintext: Vec<u8>, public_key: Vec<u8>, info_str: Vec<u8>) -> (Vec<u8>, Vec<u8>, Vec<u8>);
fn decrypt(ciphertext: Vec<u8>, private_key: Vec<u8>, encapped_key: Vec<u8>, tag: Vec<u8>, info_str: Vec<u8>) -> Vec<u8>;
}
fn encrypt(plaintext: Vec<u8>, public_key: Vec<u8>, info_str: Vec<u8>) -> CasResult<(Vec<u8>, Vec<u8>, Vec<u8>)>;
fn decrypt(ciphertext: Vec<u8>, private_key: Vec<u8>, encapped_key: Vec<u8>, tag: Vec<u8>, info_str: Vec<u8>) -> CasResult<Vec<u8>>;
}
Loading
Loading