mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-29 16:37:07 +00:00
chore(deps): migrate direct encoding deps to simd (#6690)
This commit is contained in:
@@ -50,8 +50,8 @@ aes-gcm = { workspace = true, features = ["rand_core"] }
|
||||
argon2 = { workspace = true }
|
||||
chacha20poly1305 = { workspace = true }
|
||||
rand = { workspace = true, features = ["serde"] }
|
||||
base64 = { workspace = true }
|
||||
hex = { workspace = true }
|
||||
base64-simd = { workspace = true }
|
||||
hex-simd = { workspace = true }
|
||||
sha2 = { workspace = true }
|
||||
subtle = { workspace = true }
|
||||
zeroize = { workspace = true, features = ["derive"] }
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
//! Exit status is the verdict: 0 when every check held, 1 otherwise, so a
|
||||
//! scheduled drill fails its job instead of quietly filing a bad report.
|
||||
|
||||
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
|
||||
use base64_simd::STANDARD as BASE64;
|
||||
use rustfs_kms::backup::{BackupKek, DrillDataset, DrillDisaster, DrillRequest, DrillVerdict, run_local_drill};
|
||||
use std::path::PathBuf;
|
||||
use std::process::ExitCode;
|
||||
@@ -100,7 +100,11 @@ fn disaster_from_env() -> Result<DrillDisaster, String> {
|
||||
|
||||
fn kek_from_env() -> Result<BackupKek, String> {
|
||||
let raw = Zeroizing::new(required(ENV_KEK)?);
|
||||
let decoded = Zeroizing::new(BASE64.decode(raw.trim()).map_err(|_| format!("{ENV_KEK} must be base64"))?);
|
||||
let decoded = Zeroizing::new(
|
||||
BASE64
|
||||
.decode_to_vec(raw.trim())
|
||||
.map_err(|_| format!("{ENV_KEK} must be base64"))?,
|
||||
);
|
||||
if decoded.len() != 32 {
|
||||
return Err(format!("{ENV_KEK} must decode to exactly 32 bytes"));
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64_STANDARD};
|
||||
use base64_simd::STANDARD as BASE64_STANDARD;
|
||||
use rustfs_kms::{LocalConfig, backends::local::LocalKmsClient};
|
||||
use std::io::{self, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -69,7 +69,7 @@ async fn run() -> Result<(), String> {
|
||||
.decrypt_key_material_for_export(&key_id)
|
||||
.await
|
||||
.map_err(|error| error.to_string())?;
|
||||
let encoded = Zeroizing::new(BASE64_STANDARD.encode(key_material.as_ref()));
|
||||
let encoded = Zeroizing::new(BASE64_STANDARD.encode_to_string(key_material.as_ref()));
|
||||
|
||||
let mut stdout = io::stdout().lock();
|
||||
writeln!(stdout, "{}", encoded.as_str()).map_err(|error| format!("failed to write decrypted key: {error}"))
|
||||
|
||||
@@ -305,7 +305,7 @@ pub fn redact_encryption_context(encryption_context: &HashMap<String, String>) -
|
||||
}
|
||||
|
||||
fn digest_value(value: &str) -> String {
|
||||
let digest = hex::encode(Sha256::digest(value.as_bytes()));
|
||||
let digest = hex_simd::encode_to_string(Sha256::digest(value.as_bytes()), hex_simd::AsciiCase::Lower);
|
||||
format!("{DIGEST_PREFIX}{}", &digest[..DIGEST_LEN])
|
||||
}
|
||||
|
||||
|
||||
@@ -848,8 +848,7 @@ mod tests {
|
||||
use aws_sdk_kms::config::{BehaviorVersion, Credentials, Region};
|
||||
use aws_smithy_http_client::test_util::{NeverClient, ReplayEvent, StaticReplayClient};
|
||||
use aws_smithy_types::body::SdkBody;
|
||||
use base64::Engine as _;
|
||||
use base64::engine::general_purpose::STANDARD as BASE64;
|
||||
use base64_simd::STANDARD as BASE64;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
/// AWS KMS speaks awsJson1_1; every request goes to `/` on the regional
|
||||
@@ -977,8 +976,8 @@ mod tests {
|
||||
let ciphertext = b"encrypted-data-key".to_vec();
|
||||
let (http_client, backend) = scripted_backend(vec![ok_event(serde_json::json!({
|
||||
"KeyId": "arn:aws:kms:us-east-1:111122223333:key/test-key",
|
||||
"Plaintext": BASE64.encode(&plaintext),
|
||||
"CiphertextBlob": BASE64.encode(&ciphertext),
|
||||
"Plaintext": BASE64.encode_to_string(&plaintext),
|
||||
"CiphertextBlob": BASE64.encode_to_string(&ciphertext),
|
||||
}))]);
|
||||
|
||||
let response = backend
|
||||
@@ -997,7 +996,7 @@ mod tests {
|
||||
let plaintext = b"recovered-data-key".to_vec();
|
||||
let (_http, backend) = scripted_backend(vec![ok_event(serde_json::json!({
|
||||
"KeyId": "arn:aws:kms:us-east-1:111122223333:key/test-key",
|
||||
"Plaintext": BASE64.encode(&plaintext),
|
||||
"Plaintext": BASE64.encode_to_string(&plaintext),
|
||||
"EncryptionAlgorithm": "SYMMETRIC_DEFAULT",
|
||||
}))]);
|
||||
|
||||
@@ -1061,8 +1060,8 @@ mod tests {
|
||||
error_event(400, "ThrottlingException", "rate exceeded"),
|
||||
ok_event(serde_json::json!({
|
||||
"KeyId": "test-key",
|
||||
"Plaintext": BASE64.encode([1u8; 32]),
|
||||
"CiphertextBlob": BASE64.encode(b"blob"),
|
||||
"Plaintext": BASE64.encode_to_string([1u8; 32]),
|
||||
"CiphertextBlob": BASE64.encode_to_string(b"blob"),
|
||||
})),
|
||||
]);
|
||||
|
||||
|
||||
@@ -41,8 +41,7 @@ use crate::types::{
|
||||
DescribeKeyRequest, EncryptRequest, GenerateDataKeyRequest, KeySpec, KeyState, KeyUsage, ObjectEncryptionContext,
|
||||
RewrapDataKeyRequest,
|
||||
};
|
||||
use base64::Engine as _;
|
||||
use base64::engine::general_purpose::STANDARD as BASE64;
|
||||
use base64_simd::STANDARD as BASE64;
|
||||
use rand::RngExt as _;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
@@ -285,7 +284,7 @@ async fn static_backend_stateless_contract() {
|
||||
let key_id = "static-contract-key";
|
||||
let mut raw_key = [0u8; 32];
|
||||
rand::rng().fill(&mut raw_key[..]);
|
||||
let config = KmsConfig::static_kms(key_id.to_string(), BASE64.encode(raw_key));
|
||||
let config = KmsConfig::static_kms(key_id.to_string(), BASE64.encode_to_string(raw_key));
|
||||
let static_backend = StaticKmsBackend::new(config).await.expect("static backend should build");
|
||||
let backend: &dyn KmsBackend = &static_backend;
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ use aes_gcm::{
|
||||
};
|
||||
use argon2::{Algorithm, Argon2, Params, Version};
|
||||
use async_trait::async_trait;
|
||||
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
|
||||
use base64_simd::STANDARD as BASE64;
|
||||
use jiff::Zoned;
|
||||
use rand::RngExt;
|
||||
use serde::de::{self, IgnoredAny, MapAccess, Visitor};
|
||||
@@ -1268,7 +1268,7 @@ impl LocalKmsClient {
|
||||
}
|
||||
|
||||
let encrypted_bytes = BASE64
|
||||
.decode(&stored_key.encrypted_key_material)
|
||||
.decode_to_vec(&stored_key.encrypted_key_material)
|
||||
.map_err(|e| KmsError::material_corrupt(key_id, format!("stored key material is not valid base64: {e}")))?;
|
||||
|
||||
let effective_protection = if stored_key.at_rest_protection == StoredKeyProtection::LegacyUnspecified {
|
||||
@@ -1406,13 +1406,17 @@ impl LocalKmsClient {
|
||||
.encrypt(&nonce, key_material)
|
||||
.map_err(|e| KmsError::cryptographic_error("encrypt", e.to_string()))?;
|
||||
// Encode encrypted bytes to base64 string
|
||||
(BASE64.encode(&encrypted), nonce.to_vec(), StoredKeyProtection::EncryptedMasterKey)
|
||||
(
|
||||
BASE64.encode_to_string(&encrypted),
|
||||
nonce.to_vec(),
|
||||
StoredKeyProtection::EncryptedMasterKey,
|
||||
)
|
||||
} else {
|
||||
warn!(
|
||||
key_id = %master_key.key_id,
|
||||
"Local KMS is storing key material as plaintext-dev-only because no master key is configured"
|
||||
);
|
||||
(BASE64.encode(key_material), Vec::new(), StoredKeyProtection::PlaintextDevOnly)
|
||||
(BASE64.encode_to_string(key_material), Vec::new(), StoredKeyProtection::PlaintextDevOnly)
|
||||
};
|
||||
|
||||
let stored_key = StoredMasterKey {
|
||||
@@ -2648,10 +2652,10 @@ mod tests {
|
||||
|
||||
let tampered_material = {
|
||||
let mut material = BASE64
|
||||
.decode(pristine["encrypted_key_material"].as_str().expect("material is a string"))
|
||||
.decode_to_vec(pristine["encrypted_key_material"].as_str().expect("material is a string"))
|
||||
.expect("decode pristine material");
|
||||
*material.last_mut().expect("material is not empty") ^= 0x01;
|
||||
BASE64.encode(&material)
|
||||
BASE64.encode_to_string(&material)
|
||||
};
|
||||
|
||||
type PoisonCase = (&'static str, Vec<u8>, fn(&KmsError) -> bool);
|
||||
@@ -2976,7 +2980,7 @@ mod tests {
|
||||
"created_at": "2024-01-01T00:00:00+00:00",
|
||||
"rotated_at": serde_json::Value::Null,
|
||||
"created_by": "legacy-test",
|
||||
"encrypted_key_material": BASE64.encode([7u8; 32]),
|
||||
"encrypted_key_material": BASE64.encode_to_string([7u8; 32]),
|
||||
"nonce": Vec::<u8>::new()
|
||||
});
|
||||
|
||||
|
||||
@@ -722,8 +722,7 @@ impl Default for BackendCapabilities {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::config::KmsConfig;
|
||||
use base64::Engine as _;
|
||||
use base64::engine::general_purpose::STANDARD as BASE64;
|
||||
use base64_simd::STANDARD as BASE64;
|
||||
|
||||
/// Backend that implements only the trait-mandated operations and relies
|
||||
/// on the default `capabilities` implementation.
|
||||
@@ -958,7 +957,7 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn static_backend_capabilities_golden() {
|
||||
let config = KmsConfig::static_kms("static-key".to_string(), BASE64.encode([0u8; 32]));
|
||||
let config = KmsConfig::static_kms("static-key".to_string(), BASE64.encode_to_string([0u8; 32]));
|
||||
let backend = static_kms::StaticKmsBackend::new(config)
|
||||
.await
|
||||
.expect("static backend should build");
|
||||
|
||||
@@ -434,8 +434,7 @@ mod tests {
|
||||
use crate::backends::KmsBackend as KmsBackendTrait;
|
||||
use crate::config::{BackendConfig, KmsBackend, StaticConfig};
|
||||
use crate::encryption::is_data_key_envelope;
|
||||
use base64::Engine as _;
|
||||
use base64::engine::general_purpose::STANDARD as BASE64;
|
||||
use base64_simd::STANDARD as BASE64;
|
||||
|
||||
/// Generate a random 32-byte key and return (key_id, raw_key).
|
||||
fn random_static_key(key_id: &str) -> (String, [u8; 32]) {
|
||||
@@ -447,7 +446,7 @@ mod tests {
|
||||
fn static_config(key_id: &str, raw_key: &[u8; 32]) -> StaticConfig {
|
||||
StaticConfig {
|
||||
key_id: key_id.to_string(),
|
||||
secret_key: BASE64.encode(raw_key),
|
||||
secret_key: BASE64.encode_to_string(raw_key),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ use crate::persisted_observability::{BoundedUnknownFieldName, UnknownFieldSummar
|
||||
use crate::policy::{self, AttemptError, OpClass, RetryPolicy};
|
||||
use crate::types::*;
|
||||
use async_trait::async_trait;
|
||||
use base64::{Engine as _, engine::general_purpose};
|
||||
use base64_simd::STANDARD as BASE64;
|
||||
use jiff::Zoned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
@@ -527,8 +527,8 @@ fn decode_stored_key_material(key_id: &str, encrypted_material: &str) -> Result<
|
||||
|
||||
// Mirrors `decrypt_key_material`: stored material is currently base64 without an
|
||||
// additional encryption layer.
|
||||
let key_material = general_purpose::STANDARD
|
||||
.decode(encrypted_material)
|
||||
let key_material = BASE64
|
||||
.decode_to_vec(encrypted_material)
|
||||
.map_err(|e| KmsError::material_corrupt(key_id, format!("stored key material is not valid base64: {e}")))?;
|
||||
|
||||
// Key material must be exactly 32 bytes for AES-256.
|
||||
@@ -693,7 +693,7 @@ impl VaultKmsClient {
|
||||
/// confidentiality. Any identity with KV read access to the key path can recover the
|
||||
/// plaintext master key.
|
||||
async fn encrypt_key_material(&self, key_material: &[u8]) -> Result<String> {
|
||||
Ok(general_purpose::STANDARD.encode(key_material))
|
||||
Ok(base64_simd::STANDARD.encode_to_string(key_material))
|
||||
}
|
||||
|
||||
/// Read the immutable material record of one key version.
|
||||
@@ -2405,7 +2405,7 @@ mod tests {
|
||||
tags: HashMap::new(),
|
||||
deletion_date: None,
|
||||
rotated_at: None,
|
||||
encrypted_key_material: general_purpose::STANDARD.encode([0x42u8; 32]),
|
||||
encrypted_key_material: base64_simd::STANDARD.encode_to_string([0x42u8; 32]),
|
||||
baseline_version: None,
|
||||
wrap_budget_reserved: 0,
|
||||
}
|
||||
@@ -2869,21 +2869,21 @@ mod tests {
|
||||
));
|
||||
|
||||
// Truncated material: valid base64 of fewer than 32 bytes.
|
||||
let truncated = general_purpose::STANDARD.encode([0x42u8; 16]);
|
||||
let truncated = base64_simd::STANDARD.encode_to_string([0x42u8; 16]);
|
||||
assert!(matches!(
|
||||
decode_stored_key_material("poisoned", &truncated),
|
||||
Err(KmsError::MaterialCorrupt { key_id, .. }) if key_id == "poisoned"
|
||||
));
|
||||
|
||||
// Oversized material: valid base64 of more than 32 bytes.
|
||||
let oversized = general_purpose::STANDARD.encode([0x42u8; 33]);
|
||||
let oversized = base64_simd::STANDARD.encode_to_string([0x42u8; 33]);
|
||||
assert!(matches!(
|
||||
decode_stored_key_material("poisoned", &oversized),
|
||||
Err(KmsError::MaterialCorrupt { key_id, .. }) if key_id == "poisoned"
|
||||
));
|
||||
|
||||
// Well-formed material still decodes.
|
||||
let valid = general_purpose::STANDARD.encode([0x42u8; 32]);
|
||||
let valid = base64_simd::STANDARD.encode_to_string([0x42u8; 32]);
|
||||
assert_eq!(
|
||||
decode_stored_key_material("healthy", &valid).expect("valid material must decode"),
|
||||
vec![0x42u8; 32]
|
||||
@@ -3046,7 +3046,7 @@ mod tests {
|
||||
description: None,
|
||||
metadata: HashMap::new(),
|
||||
tags: HashMap::new(),
|
||||
encrypted_key_material: general_purpose::STANDARD.encode([0x42u8; 32]),
|
||||
encrypted_key_material: base64_simd::STANDARD.encode_to_string([0x42u8; 32]),
|
||||
baseline_version: Some(1),
|
||||
deletion_date: None,
|
||||
rotated_at: None,
|
||||
@@ -3760,7 +3760,7 @@ mod tests {
|
||||
/// Base64 material distinct from `healthy_key_data`'s, standing in for the
|
||||
/// material a concurrent rotation committed.
|
||||
fn rotated_material() -> String {
|
||||
general_purpose::STANDARD.encode([0x43u8; 32])
|
||||
base64_simd::STANDARD.encode_to_string([0x43u8; 32])
|
||||
}
|
||||
|
||||
/// The issue's lost-update scenario: node A disables a key while node B's
|
||||
@@ -4358,7 +4358,7 @@ mod tests {
|
||||
let material_v2 = [0x43u8; 32];
|
||||
let record_v2 = VaultKeyVersionRecord {
|
||||
version: 2,
|
||||
encrypted_key_material: general_purpose::STANDARD.encode(material_v2),
|
||||
encrypted_key_material: base64_simd::STANDARD.encode_to_string(material_v2),
|
||||
created_at: Zoned::now(),
|
||||
};
|
||||
// A well-formed envelope wrapped under version 2 — under a reverted
|
||||
@@ -4900,8 +4900,8 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn wired_decrypt_of_pre_versioning_envelope_adds_no_request() {
|
||||
let key_data = healthy_key_data();
|
||||
let key_material = general_purpose::STANDARD
|
||||
.decode(&key_data.encrypted_key_material)
|
||||
let key_material = BASE64
|
||||
.decode_to_vec(&key_data.encrypted_key_material)
|
||||
.expect("decode fixture material");
|
||||
let (encrypted_key, nonce) = AesDekCrypto::new()
|
||||
.encrypt(&key_material, b"dek-plaintext", &[])
|
||||
|
||||
@@ -31,7 +31,7 @@ use crate::persisted_observability::{BoundedUnknownFieldName, UnknownFieldSummar
|
||||
use crate::policy::{self, AttemptError, OpClass, RetryPolicy};
|
||||
use crate::types::*;
|
||||
use async_trait::async_trait;
|
||||
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
|
||||
use base64_simd::STANDARD as BASE64;
|
||||
use jiff::Zoned;
|
||||
use moka::future::Cache;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -479,7 +479,7 @@ impl VaultTransitKmsClient {
|
||||
.map(|(key, value)| (key.clone(), value.clone()))
|
||||
.collect();
|
||||
let serialized = serde_json::to_vec(&ordered)?;
|
||||
Ok(Some(BASE64.encode(serialized)))
|
||||
Ok(Some(BASE64.encode_to_string(serialized)))
|
||||
}
|
||||
|
||||
fn map_vault_error(key_id: &str, error: vaultrs::error::ClientError, operation: &str) -> KmsError {
|
||||
@@ -524,7 +524,7 @@ impl VaultTransitKmsClient {
|
||||
plaintext: &[u8],
|
||||
encryption_context: &HashMap<String, String>,
|
||||
) -> Result<String> {
|
||||
let plaintext_b64 = BASE64.encode(plaintext);
|
||||
let plaintext_b64 = BASE64.encode_to_string(plaintext);
|
||||
let plaintext_b64 = plaintext_b64.as_str();
|
||||
let aad = Self::canonicalize_context(encryption_context)?;
|
||||
let aad = aad.as_deref();
|
||||
@@ -568,7 +568,7 @@ impl VaultTransitKmsClient {
|
||||
.await?;
|
||||
|
||||
BASE64
|
||||
.decode(response.plaintext)
|
||||
.decode_to_vec(response.plaintext)
|
||||
.map_err(|e| KmsError::cryptographic_error("base64_decode", e.to_string()))
|
||||
}
|
||||
|
||||
@@ -3031,7 +3031,7 @@ mod tests {
|
||||
ScriptedResponse::ok(kv2_write_ack()),
|
||||
// decrypt of the pre-rotation envelope; Vault owns the transit
|
||||
// crypto, so the recovered material is the responder's to hand back.
|
||||
ScriptedResponse::ok(serde_json::json!({ "plaintext": BASE64.encode(RECOVERED_DEK) })),
|
||||
ScriptedResponse::ok(serde_json::json!({ "plaintext": BASE64.encode_to_string(RECOVERED_DEK) })),
|
||||
])
|
||||
.await;
|
||||
|
||||
@@ -3243,7 +3243,7 @@ mod tests {
|
||||
// rewrap, context-bound route: latest-version read, then decrypt,
|
||||
// then re-encrypt under the newest version.
|
||||
ScriptedResponse::ok(transit_key_read_data_up_to("wired-key", 2)),
|
||||
ScriptedResponse::ok(serde_json::json!({ "plaintext": BASE64.encode(RECOVERED_DEK) })),
|
||||
ScriptedResponse::ok(serde_json::json!({ "plaintext": BASE64.encode_to_string(RECOVERED_DEK) })),
|
||||
ScriptedResponse::ok(serde_json::json!({ "ciphertext": "vault:v2:rewrapped" })),
|
||||
])
|
||||
.await;
|
||||
|
||||
@@ -945,7 +945,7 @@ async fn tree_digest(root: &Path) -> Result<ContentDigest> {
|
||||
lines.push(format!(
|
||||
"{relative}\u{1f}{}\u{1f}{modified}\u{1f}{}",
|
||||
metadata.len(),
|
||||
hex::encode(Sha256::digest(&content))
|
||||
hex_simd::encode_to_string(Sha256::digest(&content), hex_simd::AsciiCase::Lower)
|
||||
));
|
||||
}
|
||||
lines.sort();
|
||||
@@ -1199,7 +1199,7 @@ mod tests {
|
||||
let text = String::from_utf8(encoded.clone()).expect("evidence is utf-8");
|
||||
assert!(!text.contains(DRILL_MASTER_KEY), "the evidence must not carry the master key");
|
||||
assert!(
|
||||
!text.contains(&hex::encode([0x37u8; 32])),
|
||||
!text.contains(&hex_simd::encode_to_string([0x37u8; 32], hex_simd::AsciiCase::Lower)),
|
||||
"the evidence must not carry backup KEK material"
|
||||
);
|
||||
|
||||
|
||||
@@ -568,7 +568,10 @@ pub(crate) fn compute_master_key_verifier(master_key: &str, salt: Option<&[u8]>,
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(&framing);
|
||||
hasher.update(derived.as_slice());
|
||||
Ok(format!("{prefix}{}", hex::encode(hasher.finalize())))
|
||||
Ok(format!(
|
||||
"{prefix}{}",
|
||||
hex_simd::encode_to_string(hasher.finalize(), hex_simd::AsciiCase::Lower)
|
||||
))
|
||||
}
|
||||
|
||||
/// The bundle-level protection label is the weakest state observed across
|
||||
|
||||
@@ -72,7 +72,7 @@ use aes_gcm::{
|
||||
Aes256Gcm, Nonce,
|
||||
aead::{Aead, KeyInit},
|
||||
};
|
||||
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
|
||||
use base64_simd::STANDARD as BASE64;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::{Path, PathBuf};
|
||||
use tokio::fs;
|
||||
@@ -640,7 +640,7 @@ fn decode_key_record(
|
||||
return Err(BackupError::corrupted(format!("bundled key record '{stem}' carries no key material")).into());
|
||||
}
|
||||
let material =
|
||||
Zeroizing::new(BASE64.decode(&probe.encrypted_key_material).map_err(|error| {
|
||||
Zeroizing::new(BASE64.decode_to_vec(&probe.encrypted_key_material).map_err(|error| {
|
||||
BackupError::corrupted(format!("bundled key record '{stem}' material is not valid base64: {error}"))
|
||||
})?);
|
||||
if !allowed_modes.contains(&protection_mode(probe.at_rest_protection)) {
|
||||
|
||||
@@ -69,7 +69,7 @@ impl ContentDigest {
|
||||
pub fn sha256_of(bytes: &[u8]) -> Self {
|
||||
Self {
|
||||
algorithm: DigestAlgorithm::Sha256,
|
||||
hex: hex::encode(Sha256::digest(bytes)),
|
||||
hex: hex_simd::encode_to_string(Sha256::digest(bytes), hex_simd::AsciiCase::Lower),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -367,9 +367,8 @@ impl StaticConfig {
|
||||
/// Decode the base64-encoded secret key into raw bytes.
|
||||
/// Returns an error if the key is not valid base64 or is not exactly 32 bytes.
|
||||
pub fn decode_key(&self) -> Result<[u8; 32]> {
|
||||
use base64::Engine as _;
|
||||
let bytes = base64::engine::general_purpose::STANDARD
|
||||
.decode(&self.secret_key)
|
||||
let bytes = base64_simd::STANDARD
|
||||
.decode_to_vec(&self.secret_key)
|
||||
.map_err(|e| KmsError::configuration_error(format!("Static KMS secret key is not valid base64: {e}")))?;
|
||||
if bytes.len() != 32 {
|
||||
return Err(KmsError::configuration_error(format!(
|
||||
@@ -1963,9 +1962,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn static_kms_config_serialization_does_not_expose_key_material() {
|
||||
use base64::Engine as _;
|
||||
|
||||
let encoded_key = base64::engine::general_purpose::STANDARD.encode([0x5au8; 32]);
|
||||
let encoded_key = base64_simd::STANDARD.encode_to_string([0x5au8; 32]);
|
||||
let config = KmsConfig::static_kms("static-key".to_string(), encoded_key.clone());
|
||||
|
||||
let serialized = serde_json::to_string(&config).expect("static KMS config should serialize");
|
||||
@@ -2569,14 +2566,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_from_env_reads_static_secret_file_and_sets_default_key() {
|
||||
use base64::Engine as _;
|
||||
|
||||
let temp_dir = TempDir::new().expect("create temp dir for static KMS secret");
|
||||
let secret_path = temp_dir.path().join("static-kms-secret");
|
||||
// Named `*_key_b64` (not `*_secret`) so the logging-guardrails check does not
|
||||
// flag these fixture interpolations as secrets leaking into log strings.
|
||||
let file_key_b64 = base64::engine::general_purpose::STANDARD.encode([7u8; 32]);
|
||||
let env_key_b64 = base64::engine::general_purpose::STANDARD.encode([9u8; 32]);
|
||||
let file_key_b64 = base64_simd::STANDARD.encode_to_string([7u8; 32]);
|
||||
let env_key_b64 = base64_simd::STANDARD.encode_to_string([9u8; 32]);
|
||||
std::fs::write(&secret_path, format!("file-key:{file_key_b64}\n")).expect("write static KMS secret file");
|
||||
|
||||
with_vars(
|
||||
|
||||
@@ -46,8 +46,7 @@ use crate::error::{KmsError, Result};
|
||||
use aes_gcm::aead::{Aead, Payload};
|
||||
use aes_gcm::{Aes256Gcm, Key, KeyInit, Nonce};
|
||||
use argon2::{Algorithm, Argon2, Params, Version};
|
||||
use base64::Engine as _;
|
||||
use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
|
||||
use base64_simd::STANDARD as BASE64_STANDARD;
|
||||
use rand::RngExt;
|
||||
use serde_json::Value;
|
||||
use sha2::{Digest, Sha256};
|
||||
@@ -284,14 +283,16 @@ fn seal_value(label: &str, plaintext: &str, secret: &str) -> Result<String> {
|
||||
payload.extend_from_slice(&salt);
|
||||
payload.extend_from_slice(&nonce);
|
||||
payload.extend_from_slice(&ciphertext);
|
||||
Ok(format!("{SEALED_VALUE_PREFIX}{}", BASE64_STANDARD.encode(payload)))
|
||||
Ok(format!("{SEALED_VALUE_PREFIX}{}", BASE64_STANDARD.encode_to_string(payload)))
|
||||
}
|
||||
|
||||
fn open_value(label: &str, sealed: &str, secret: &str) -> Result<String> {
|
||||
let encoded = sealed
|
||||
.strip_prefix(SEALED_VALUE_PREFIX)
|
||||
.expect("caller checks the sealed prefix");
|
||||
let payload = BASE64_STANDARD.decode(encoded).map_err(|_| sealed_value_unreadable(label))?;
|
||||
let payload = BASE64_STANDARD
|
||||
.decode_to_vec(encoded)
|
||||
.map_err(|_| sealed_value_unreadable(label))?;
|
||||
if payload.len() <= LOCAL_KMS_MASTER_KEY_SALT_LEN + NONCE_LEN {
|
||||
return Err(sealed_value_unreadable(label));
|
||||
}
|
||||
|
||||
@@ -675,7 +675,6 @@ mod tests {
|
||||
use crate::error::KmsError;
|
||||
use crate::types::{KeyMetadata, KeySpec, KeyState, KeyStatus, KeyUsage};
|
||||
use async_trait::async_trait;
|
||||
use base64::Engine as _;
|
||||
use jiff::Zoned;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
@@ -1110,8 +1109,13 @@ mod tests {
|
||||
.await
|
||||
.expect("enable should succeed");
|
||||
|
||||
let base64 = base64::engine::general_purpose::STANDARD;
|
||||
let encodings = |bytes: &[u8]| vec![hex::encode(bytes), base64.encode(bytes)];
|
||||
let base64 = base64_simd::STANDARD;
|
||||
let encodings = |bytes: &[u8]| {
|
||||
vec![
|
||||
hex_simd::encode_to_string(bytes, hex_simd::AsciiCase::Lower),
|
||||
base64.encode_to_string(bytes),
|
||||
]
|
||||
};
|
||||
let mut forbidden = vec![grant_token.to_string()];
|
||||
forbidden.extend(encodings(&data_key.plaintext_key));
|
||||
forbidden.extend(encodings(&decrypted.plaintext));
|
||||
|
||||
@@ -551,7 +551,6 @@ mod tests {
|
||||
ListKeysRequest, ListKeysResponse,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use base64::Engine as _;
|
||||
use metrics_util::MetricKind;
|
||||
use metrics_util::debugging::{DebugValue, DebuggingRecorder};
|
||||
use std::future::Future;
|
||||
@@ -562,8 +561,7 @@ mod tests {
|
||||
}
|
||||
|
||||
async fn static_backend() -> Arc<dyn KmsBackend> {
|
||||
let config =
|
||||
KmsConfig::static_kms("static-key".to_string(), base64::engine::general_purpose::STANDARD.encode([0x42u8; 32]));
|
||||
let config = KmsConfig::static_kms("static-key".to_string(), base64_simd::STANDARD.encode_to_string([0x42u8; 32]));
|
||||
Arc::new(StaticKmsBackend::new(config).await.expect("static backend should build"))
|
||||
}
|
||||
|
||||
|
||||
+10
-14
@@ -23,7 +23,6 @@ use crate::encryption::context_aad;
|
||||
use crate::error::{KmsError, Result};
|
||||
use crate::manager::KmsManager;
|
||||
use crate::types::*;
|
||||
use base64::Engine;
|
||||
use jiff::Zoned;
|
||||
use md5::{Digest as Md5Digest, Md5};
|
||||
use rand::random;
|
||||
@@ -40,7 +39,7 @@ use zeroize::Zeroize;
|
||||
fn md5_hex(input: impl AsRef<[u8]>) -> String {
|
||||
let mut hasher = Md5::new();
|
||||
hasher.update(input.as_ref());
|
||||
hex::encode(hasher.finalize())
|
||||
hex_simd::encode_to_string(hasher.finalize(), hex_simd::AsciiCase::Lower)
|
||||
}
|
||||
|
||||
/// Data key for object encryption
|
||||
@@ -836,19 +835,16 @@ impl ObjectEncryptionService {
|
||||
// Internal headers for decryption
|
||||
headers.insert(
|
||||
INTERNAL_ENCRYPTION_IV_HEADER.to_string(),
|
||||
base64::engine::general_purpose::STANDARD.encode(&metadata.iv),
|
||||
base64_simd::STANDARD.encode_to_string(&metadata.iv),
|
||||
);
|
||||
|
||||
if let Some(ref tag) = metadata.tag {
|
||||
headers.insert(
|
||||
INTERNAL_ENCRYPTION_TAG_HEADER.to_string(),
|
||||
base64::engine::general_purpose::STANDARD.encode(tag),
|
||||
);
|
||||
headers.insert(INTERNAL_ENCRYPTION_TAG_HEADER.to_string(), base64_simd::STANDARD.encode_to_string(tag));
|
||||
}
|
||||
|
||||
headers.insert(
|
||||
INTERNAL_ENCRYPTION_KEY_HEADER.to_string(),
|
||||
base64::engine::general_purpose::STANDARD.encode(&metadata.encrypted_data_key),
|
||||
base64_simd::STANDARD.encode_to_string(&metadata.encrypted_data_key),
|
||||
);
|
||||
|
||||
// Whatever the object was sealed under is what gets stored: for a
|
||||
@@ -906,14 +902,14 @@ impl ObjectEncryptionService {
|
||||
let iv = headers
|
||||
.get(INTERNAL_ENCRYPTION_IV_HEADER)
|
||||
.ok_or_else(|| KmsError::validation_error("Missing IV header"))?;
|
||||
let iv = base64::engine::general_purpose::STANDARD
|
||||
.decode(iv)
|
||||
let iv = base64_simd::STANDARD
|
||||
.decode_to_vec(iv)
|
||||
.map_err(|e| KmsError::validation_error(format!("Invalid IV: {e}")))?;
|
||||
|
||||
let tag = if let Some(tag_str) = headers.get(INTERNAL_ENCRYPTION_TAG_HEADER) {
|
||||
Some(
|
||||
base64::engine::general_purpose::STANDARD
|
||||
.decode(tag_str)
|
||||
base64_simd::STANDARD
|
||||
.decode_to_vec(tag_str)
|
||||
.map_err(|e| KmsError::validation_error(format!("Invalid tag: {e}")))?,
|
||||
)
|
||||
} else {
|
||||
@@ -921,8 +917,8 @@ impl ObjectEncryptionService {
|
||||
};
|
||||
|
||||
let encrypted_data_key = if let Some(key_str) = headers.get(INTERNAL_ENCRYPTION_KEY_HEADER) {
|
||||
base64::engine::general_purpose::STANDARD
|
||||
.decode(key_str)
|
||||
base64_simd::STANDARD
|
||||
.decode_to_vec(key_str)
|
||||
.map_err(|e| KmsError::validation_error(format!("Invalid encrypted key: {e}")))?
|
||||
} else {
|
||||
Vec::new() // Empty for SSE-C
|
||||
|
||||
@@ -763,10 +763,10 @@ pub async fn get_global_encryption_service() -> Option<Arc<ObjectEncryptionServi
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64_STANDARD};
|
||||
use base64_simd::STANDARD as BASE64_STANDARD;
|
||||
|
||||
fn static_config(key_id: &str, fill: u8) -> KmsConfig {
|
||||
KmsConfig::static_kms(key_id.to_string(), BASE64_STANDARD.encode([fill; 32]))
|
||||
KmsConfig::static_kms(key_id.to_string(), BASE64_STANDARD.encode_to_string([fill; 32]))
|
||||
}
|
||||
|
||||
/// End-to-end wiring check for the AWS backend: an admin configure request
|
||||
@@ -822,7 +822,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn redacted_config_omits_static_key_material() {
|
||||
let manager = KmsServiceManager::new();
|
||||
let encoded_key = base64::engine::general_purpose::STANDARD.encode([0x5au8; 32]);
|
||||
let encoded_key = base64_simd::STANDARD.encode_to_string([0x5au8; 32]);
|
||||
manager
|
||||
.configure(KmsConfig::static_kms("static-key".to_string(), encoded_key))
|
||||
.await
|
||||
@@ -1020,7 +1020,6 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn configure_cannot_replace_existing_local_backend() {
|
||||
use base64::Engine as _;
|
||||
use tempfile::TempDir;
|
||||
|
||||
let key_dir = TempDir::new().expect("create local KMS directory");
|
||||
@@ -1029,7 +1028,7 @@ mod tests {
|
||||
let manager = KmsServiceManager::new();
|
||||
manager.configure(local.clone()).await.expect("configure local KMS");
|
||||
|
||||
let encoded_key = base64::engine::general_purpose::STANDARD.encode([0x5au8; 32]);
|
||||
let encoded_key = base64_simd::STANDARD.encode_to_string([0x5au8; 32]);
|
||||
let error = manager
|
||||
.configure(KmsConfig::static_kms("static-key".to_string(), encoded_key))
|
||||
.await
|
||||
|
||||
@@ -451,6 +451,5 @@ async fn harness_restart_brings_the_service_back_over_the_same_state() {
|
||||
}
|
||||
|
||||
fn base64_of(bytes: &[u8]) -> String {
|
||||
use base64::Engine as _;
|
||||
base64::engine::general_purpose::STANDARD.encode(bytes)
|
||||
base64_simd::STANDARD.encode_to_string(bytes)
|
||||
}
|
||||
|
||||
@@ -667,7 +667,7 @@ async fn sse_c_round_trips_and_rejects_the_wrong_key() {
|
||||
async fn sse_c_validates_the_supplied_key_md5() {
|
||||
let (_kms, service) = service_with_key("sse-c-md5-unused").await;
|
||||
let customer_key = [0x33u8; 32];
|
||||
let correct_md5 = hex::encode(md5_of(&customer_key));
|
||||
let correct_md5 = hex_simd::encode_to_string(md5_of(&customer_key), hex_simd::AsciiCase::Lower);
|
||||
|
||||
service
|
||||
.encrypt_object_with_customer_key(BUCKET, "md5-ok.bin", payload(64).as_slice(), &customer_key, Some(&correct_md5))
|
||||
|
||||
@@ -34,8 +34,7 @@ use std::future::Future;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use base64::Engine as _;
|
||||
use base64::engine::general_purpose::STANDARD as BASE64;
|
||||
use base64_simd::STANDARD as BASE64;
|
||||
use rustfs_kms::backends::BackendCapabilities;
|
||||
use rustfs_kms::{
|
||||
CreateKeyRequest, DeleteKeyRequest, KeyUsage, KmsConfig, KmsError, KmsManager, KmsServiceManager, KmsServiceStatus,
|
||||
@@ -51,7 +50,7 @@ pub const STATIC_KEY_ID: &str = "behavior-static-key";
|
||||
/// Fixed rather than random so a failure is reproducible; it is test-only
|
||||
/// material and never leaves this crate's test binaries.
|
||||
pub fn static_secret_key() -> String {
|
||||
BASE64.encode([0x5au8; 32])
|
||||
BASE64.encode_to_string([0x5au8; 32])
|
||||
}
|
||||
|
||||
/// Which backend a harness instance is running.
|
||||
|
||||
Reference in New Issue
Block a user