add iam system

add iam store

feat: add crypto crate

introduce decrypt_data and encrypt_data functions

Signed-off-by: bestgopher <84328409@qq.com>
This commit is contained in:
bestgopher
2024-10-14 23:11:49 +08:00
parent 8519a596ec
commit bb2f765e5d
61 changed files with 5319 additions and 64 deletions
+11
View File
@@ -0,0 +1,11 @@
#[cfg(not(feature = "fips"))]
mod aes;
#[cfg(any(test, feature = "crypto"))]
pub(crate) mod id;
pub(crate) mod decrypt;
pub(crate) mod encrypt;
#[cfg(test)]
mod tests;
+18
View File
@@ -0,0 +1,18 @@
pub fn native_aes() -> bool {
cfg_if::cfg_if! {
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
std::is_x86_feature_detected!("aes") && std::is_x86_feature_detected!("pclmulqdq")
} else if #[cfg(target_arch = "aarch64")] {
std::arch::is_aarch64_feature_detected!("aes")
} else if #[cfg(target_arch = "powerpc64")] {
false
} else if #[cfg(target_arch = "s390x")] {
std::is_s390x_feature_detected!("aes")
&& std::is_s390x_feature_detected!("aescbc")
&& std::is_s390x_feature_detected!("aesctr")
&& (std::is_s390x_feature_detected!("aesgcm") || std::is_s390x_feature_detected!("ghash"))
} else {
false
}
}
}
+41
View File
@@ -0,0 +1,41 @@
#[cfg(any(test, feature = "crypto"))]
pub fn decrypt_data(password: &[u8], data: &[u8]) -> Result<Vec<u8>, crate::Error> {
use crate::encdec::id::ID;
use aes_gcm::{Aes256Gcm, KeyInit as _};
use chacha20poly1305::ChaCha20Poly1305;
// 32: salt
// 1: id
// 12: nonce
const HEADER_LENGTH: usize = 45;
if data.len() < HEADER_LENGTH {
return Err(Error::ErrUnexpectedHeader);
}
let (salt, id, nonce) = (&data[..32], ID::try_from(data[32])?, &data[33..45]);
let data = &data[HEADER_LENGTH..];
match id {
ID::Argon2idChaCHa20Poly1305 => {
let key = id.get_key(password, salt)?;
decryp(ChaCha20Poly1305::new_from_slice(&key)?, nonce, data)
}
_ => {
let key = id.get_key(password, salt)?;
decryp(Aes256Gcm::new_from_slice(&key)?, nonce, data)
}
}
}
#[cfg(any(test, feature = "crypto"))]
#[inline]
fn decryp<T: aes_gcm::aead::Aead>(stream: T, nonce: &[u8], data: &[u8]) -> Result<Vec<u8>, crate::Error> {
stream
.decrypt(aes_gcm::Nonce::from_slice(nonce), data)
.map_err(Error::ErrDecryptFailed)
}
#[cfg(all(not(test), not(feature = "crypto")))]
pub fn decrypt_data(_password: &[u8], data: &[u8]) -> Result<Vec<u8>, crate::Error> {
Ok(data.to_vec())
}
+53
View File
@@ -0,0 +1,53 @@
#[cfg(any(test, feature = "crypto"))]
pub fn encrypt_data(password: &[u8], data: &[u8]) -> Result<Vec<u8>, crate::Error> {
use crate::encdec::id::ID;
use aes_gcm::Aes256Gcm;
use aes_gcm::KeyInit as _;
let salt: [u8; 32] = random();
#[cfg(feature = "fips")]
let id = ID::Pbkdf2AESGCM;
#[cfg(not(feature = "fips"))]
let id = if native_aes() {
ID::Argon2idAESGCM
} else {
ID::Argon2idChaCHa20Poly1305
};
let key = id.get_key(password, &salt)?;
#[cfg(feature = "fips")]
{
encrypt(Aes256Gcm::new_from_slice(&key)?, &salt, id, data)
}
#[cfg(not(feature = "fips"))]
{
if native_aes() {
encrypt(Aes256Gcm::new_from_slice(&key)?, &salt, id, data)
} else {
encrypt(ChaCha20Poly1305::new_from_slice(&key)?, &salt, id, data)
}
}
}
#[cfg(any(test, feature = "crypto"))]
fn encrypt<T: aes_gcm::aead::Aead>(stream: T, salt: &[u8], id: ID, data: &[u8]) -> Result<Vec<u8>, crate::Error> {
let nonce = T::generate_nonce(rand::thread_rng());
let encryptor = stream.encrypt(&nonce, data).map_err(Error::ErrEncryptFailed)?;
let mut ciphertext = Vec::with_capacity(salt.len() + 1 + nonce.len() + encryptor.len());
ciphertext.extend_from_slice(salt);
ciphertext.push(id as u8);
ciphertext.extend_from_slice(nonce.as_slice());
ciphertext.extend_from_slice(&encryptor);
Ok(ciphertext)
}
#[cfg(all(not(test), not(feature = "crypto")))]
pub fn encrypt_data(_password: &[u8], data: &[u8]) -> Result<Vec<u8>, crate::Error> {
Ok(data.to_vec())
}
+39
View File
@@ -0,0 +1,39 @@
use argon2::{Algorithm, Argon2, Params, Version};
use pbkdf2::pbkdf2_hmac;
use sha2::Sha256;
#[repr(u8)]
pub(crate) enum ID {
Argon2idAESGCM = 0x00,
Argon2idChaCHa20Poly1305 = 0x01,
Pbkdf2AESGCM = 0x02,
}
impl TryFrom<u8> for ID {
type Error = crate::Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x00 => Ok(Self::Argon2idAESGCM),
0x01 => Ok(Self::Argon2idChaCHa20Poly1305),
0x02 => Ok(Self::Pbkdf2AESGCM),
_ => Err(crate::Error::ErrInvalidAlgID(value)),
}
}
}
impl ID {
pub(crate) fn get_key(&self, password: &[u8], salt: &[u8]) -> Result<[u8; 32], crate::Error> {
let mut key = [0u8; 32];
match self {
ID::Pbkdf2AESGCM => pbkdf2_hmac::<Sha256>(password, salt, 8192, &mut key),
_ => {
let params = Params::new(64 * 1024, 1, 4, Some(32))?;
let argon_2id = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
let mut key = vec![0u8; 32];
argon_2id.hash_password_into(password, salt, &mut key)?;
}
}
Ok(key)
}
}
+14
View File
@@ -0,0 +1,14 @@
use crate::{decrypt_data, encrypt_data};
const PASSWORD: &[u8] = "test_password".as_bytes();
#[test_case::test_case("hello world".as_bytes())]
#[test_case::test_case(&[])]
#[test_case::test_case(&[1, 2, 3])]
#[test_case::test_case(&[3, 2, 1])]
fn test(input: &[u8]) -> Result<(), crate::Error> {
let encrypted = encrypt_data(PASSWORD, input)?;
let decrypted = decrypt_data(PASSWORD, &encrypted)?;
assert_eq!(input, decrypted, "input is not equal output");
Ok(())
}
+28
View File
@@ -0,0 +1,28 @@
use sha2::digest::InvalidLength;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("unexpected header")]
ErrUnexpectedHeader,
#[error("invalid encryption algorithm ID: {0}")]
ErrInvalidAlgID(u8),
#[error("{0}")]
ErrInvalidLength(#[from] InvalidLength),
#[cfg(any(test, feature = "crypto"))]
#[error("encrypt failed")]
ErrEncryptFailed(aes_gcm::aead::Error),
#[cfg(any(test, feature = "crypto"))]
#[error("decrypt failed")]
ErrDecryptFailed(aes_gcm::aead::Error),
#[cfg(any(test, feature = "crypto"))]
#[error("argon2 err: {0}")]
ErrArgon2(#[from] argon2::Error),
#[error("jwt err: {0}")]
ErrJwt(#[from] jsonwebtoken::errors::Error),
}
+6
View File
@@ -0,0 +1,6 @@
pub mod decode;
pub mod encode;
pub use serde_json::Value as Claims;
#[cfg(test)]
mod tests;
+12
View File
@@ -0,0 +1,12 @@
use jsonwebtoken::{Algorithm, DecodingKey, TokenData, Validation};
use crate::jwt::Claims;
use crate::Error;
pub fn decode(token: &str, token_secret: &[u8]) -> Result<TokenData<Claims>, Error> {
Ok(jsonwebtoken::decode(
token,
&DecodingKey::from_secret(token_secret),
&Validation::new(Algorithm::HS512),
)?)
}
+12
View File
@@ -0,0 +1,12 @@
use jsonwebtoken::{Algorithm, EncodingKey, Header};
use crate::jwt::Claims;
use crate::Error;
pub fn encode(token_secret: &[u8], claims: &Claims) -> Result<String, Error> {
Ok(jsonwebtoken::encode(
&Header::new(Algorithm::HS512),
claims,
&EncodingKey::from_secret(token_secret),
)?)
}
+16
View File
@@ -0,0 +1,16 @@
use time::OffsetDateTime;
use super::{decode::decode, encode::encode};
#[test]
fn test() {
let claims = serde_json::json!({
"exp": OffsetDateTime::now_utc().unix_timestamp() + 1000,
"aaa": 1,
"bbb": "bbb"
});
let jwt_token = encode(b"aaaa", &claims).unwrap();
let new_claims = decode(&jwt_token, b"aaaa").unwrap();
assert_eq!(new_claims.claims, claims);
}
+9
View File
@@ -0,0 +1,9 @@
mod encdec;
mod error;
mod jwt;
pub use encdec::decrypt::decrypt_data;
pub use encdec::encrypt::encrypt_data;
pub use error::Error;
pub use jwt::decode::decode as jwt_decode;
pub use jwt::encode::encode as jwt_encode;