diff --git a/Cargo.lock b/Cargo.lock index 885bf9a3a..e655da48b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7871,6 +7871,7 @@ dependencies = [ "rand 0.10.1", "reqwest 0.13.3", "rmp-serde", + "rsa 0.10.0-rc.18", "rust-embed", "rustfs-appauth", "rustfs-audit", diff --git a/rustfs/Cargo.toml b/rustfs/Cargo.toml index a34d7fe15..e5bb5c650 100644 --- a/rustfs/Cargo.toml +++ b/rustfs/Cargo.toml @@ -198,6 +198,7 @@ tokio = { workspace = true, features = ["test-util"] } temp-env = { workspace = true, features = ["async_closure"] } tracing-subscriber = { workspace = true } opentelemetry_sdk = { workspace = true } +rsa = { workspace = true } [build-dependencies] http.workspace = true diff --git a/rustfs/src/license.rs b/rustfs/src/license.rs index bfd2d9d5b..2ae596e17 100644 --- a/rustfs/src/license.rs +++ b/rustfs/src/license.rs @@ -324,6 +324,11 @@ pub fn license_check() -> Result<()> { #[cfg(test)] mod tests { use super::*; + use rsa::{ + RsaPrivateKey, RsaPublicKey, + pkcs8::{EncodePrivateKey, EncodePublicKey, LineEnding}, + }; + use rustfs_appauth::token::sign_license_token; use serial_test::serial; #[test] @@ -354,6 +359,32 @@ mod tests { }); } + #[test] + #[serial] + fn appauth_verifier_accepts_signed_license_with_trimmed_public_key() { + let mut rng = rand::rng(); + let private_key = RsaPrivateKey::new(&mut rng, 2048).expect("private key should be generated"); + let public_key = RsaPublicKey::from(&private_key); + let private_key_pem = private_key.to_pkcs8_pem(LineEnding::LF).expect("private key should encode"); + let public_key_pem = public_key + .to_public_key_pem(LineEnding::LF) + .expect("public key should encode"); + let expected = Token { + name: "test_app".to_string(), + expired: 100, + }; + let signed_license = sign_license_token(&expected, &private_key_pem).expect("license should sign"); + let public_key_env = format!(" \n{public_key_pem}\t "); + + let actual = temp_env::with_var(rustfs_config::ENV_RUSTFS_LICENSE_PUBLIC_KEY, Some(public_key_env), || { + AppAuthLicenseVerifier.validate(&signed_license, 0) + }) + .expect("signed license should validate with env public key"); + + assert_eq!(expected.name, actual.name); + assert_eq!(expected.expired, actual.expired); + } + fn assert_license_public_key_error(result: LicenseResult) { let err = result.expect_err("license verification should fail without a public key"); let LicenseError::Invalid(message) = err else {