mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-04 20:37:43 +00:00
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:
@@ -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;
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
@@ -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),
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod decode;
|
||||
pub mod encode;
|
||||
pub use serde_json::Value as Claims;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
@@ -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),
|
||||
)?)
|
||||
}
|
||||
@@ -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),
|
||||
)?)
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user