From fcb2962ec73b0e6be9c63aa027b130faf0b20c2c Mon Sep 17 00:00:00 2001 From: "Edward J. Jinotti" Date: Mon, 20 Jul 2026 17:28:13 -0400 Subject: [PATCH 1/5] gpt: accept pkcs1 pub keys --- CHANGELOG.md | 4 +++ Cargo.toml | 2 +- README.md | 6 ++++- src/verifier.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 74 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a34937..2e54c17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.6.1 +- Accept RSA public keys encoded as either PKCS#1 (`BEGIN RSA PUBLIC KEY`) or + SubjectPublicKeyInfo (`BEGIN PUBLIC KEY`) PEM. + # 0.6.0 - Normalize provided private key string to the correct PEM format. - Update thiserror to version 2. diff --git a/Cargo.toml b/Cargo.toml index 86d8479..bdb0d06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mauth-core" -version = "0.6.0" +version = "0.6.1" edition = "2021" authors = ["Medidata Solutions "] description = "Generate and verify Medidata MAuth protocol signatures" diff --git a/README.md b/README.md index 7bdf7a7..3bc8bde 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,13 @@ Add the following to your `Cargo.toml`: ```toml [dependencies] -mauth-core = "0.5" +mauth-core = "0.6" ``` +`Signer::new` accepts PKCS#1 RSA private key PEM. `Verifier::new` accepts RSA +public key PEM encoded as either SubjectPublicKeyInfo (`BEGIN PUBLIC KEY`) or +PKCS#1 (`BEGIN RSA PUBLIC KEY`). + Here is an example of generating and verifying a signature: ```rust diff --git a/src/verifier.rs b/src/verifier.rs index c334940..0d3139a 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -1,5 +1,6 @@ use crate::{error::Error, signable::Signable}; use base64::{engine::general_purpose, Engine as _}; +use rsa::pkcs1::DecodeRsaPublicKey; use rsa::pkcs1v15::Signature; use rsa::pkcs8::DecodePublicKey; use rsa::RsaPublicKey; @@ -14,11 +15,14 @@ pub struct Verifier { } impl Verifier { - /// Initialize a new verifier with the source app UUID and the public key. The format of the public key - /// should be a raw RSA public key in the public key PEM format, as generated by `openssl rsa -pubout -out`. - /// An error will be returned if the input data is unable to be parsed as a public key. The `app_uuid` - /// is expected to be a valid UUID, however this is not checked. If you pass something other than - /// a valid UUID, no error will be returned, but none of the signatures will be able to be validated. + /// Initialize a new verifier with the source app UUID and public key. + /// + /// The public key can be either SubjectPublicKeyInfo PEM (`BEGIN PUBLIC KEY`), as generated by + /// `openssl rsa -pubout`, or PKCS#1 PEM (`BEGIN RSA PUBLIC KEY`), as generated by + /// `openssl rsa -RSAPublicKey_out`. An error will be returned if the input data cannot be parsed + /// as either public key format. The `app_uuid` is expected to be a valid UUID, however this is + /// not checked. If you pass something other than a valid UUID, no error will be returned, but + /// none of the signatures will be able to be validated. /// /// ``` /// # use mauth_core::verifier::Verifier; @@ -27,7 +31,9 @@ impl Verifier { /// assert!(verifier.is_ok()); /// ``` pub fn new(app_uuid: impl Into, public_key_data: String) -> Result { - let public_key = RsaPublicKey::from_public_key_pem(&public_key_data)?; + let public_key = RsaPublicKey::from_public_key_pem(&public_key_data).or_else(|_| { + RsaPublicKey::from_pkcs1_pem(&public_key_data).map_err(spki::Error::from) + })?; let verifying_key = rsa::pkcs1v15::VerifyingKey::::new(public_key.to_owned()); Ok(Self { @@ -93,3 +99,55 @@ impl Verifier { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + use rsa::pkcs1::EncodeRsaPublicKey; + use rsa::pkcs8::{EncodePublicKey, LineEnding}; + use rsa::BigUint; + + const APP_UUID: &str = "101c139a-236c-11ef-b5e3-125eb8485a60"; + + fn public_key() -> RsaPublicKey { + let modulus = BigUint::from_bytes_be(&[0xff; 256]); + let public_exponent = BigUint::from(65_537_u32); + RsaPublicKey::new(modulus, public_exponent).unwrap() + } + + #[test] + fn new_accepts_subject_public_key_info_pem() { + let public_key = public_key(); + let public_key_pem = public_key.to_public_key_pem(LineEnding::LF).unwrap(); + + let verifier = Verifier::new(APP_UUID, public_key_pem).unwrap(); + + assert_eq!(verifier.public_key, public_key); + } + + #[test] + fn new_accepts_pkcs1_public_key_pem() { + let public_key = public_key(); + let public_key_pem = public_key.to_pkcs1_pem(LineEnding::LF).unwrap(); + + let verifier = Verifier::new(APP_UUID, public_key_pem).unwrap(); + + assert_eq!(verifier.public_key, public_key); + } + + #[test] + fn new_rejects_malformed_public_key_pem_as_a_public_key_error() { + let malformed_public_keys = [ + "not a PEM key", + "-----BEGIN PUBLIC KEY-----\ninvalid\n-----END PUBLIC KEY-----", + "-----BEGIN RSA PUBLIC KEY-----\ninvalid\n-----END RSA PUBLIC KEY-----", + ]; + + for malformed_public_key in malformed_public_keys { + assert!(matches!( + Verifier::new(APP_UUID, malformed_public_key.to_owned()), + Err(Error::PublicKeyDecodeError(_)) + )); + } + } +} From 23769bbc7a4638dc345137c93c8d20f444189155 Mon Sep 17 00:00:00 2001 From: "Edward J. Jinotti" Date: Mon, 20 Jul 2026 17:37:16 -0400 Subject: [PATCH 2/5] gpt: clippy --- src/signable.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/signable.rs b/src/signable.rs index e9f7ca8..3105a73 100644 --- a/src/signable.rs +++ b/src/signable.rs @@ -63,9 +63,9 @@ impl<'a> Signable<'a> { self.verb, Self::normalize_url(&self.path), body_digest, - &self.app_uuid, - &self.timestamp, - &encoded_query + self.app_uuid, + self.timestamp, + encoded_query ) .into_bytes()) } @@ -88,7 +88,7 @@ impl<'a> Signable<'a> { .join("&")) } - fn normalize_url(url: &str) -> Cow { + fn normalize_url(url: &str) -> Cow<'_, str> { if url.contains("//") || url.contains('%') || url.contains("/.") { let url = SQUEEZE_REGEX.replace_all(url, "/"); let url = PERCENT_CASE_REGEX.replace_all(&url, |c: &Captures| c[0].to_uppercase()); From 859e7f944007bf4b29ce87c01608578391f5116f Mon Sep 17 00:00:00 2001 From: "Edward J. Jinotti" Date: Mon, 20 Jul 2026 17:51:30 -0400 Subject: [PATCH 3/5] factbook & fossa --- .github/workflows/fossa.yml | 16 ++++++++++++++++ factbook.yaml | 3 +++ 2 files changed, 19 insertions(+) create mode 100644 .github/workflows/fossa.yml diff --git a/.github/workflows/fossa.yml b/.github/workflows/fossa.yml new file mode 100644 index 0000000..25310f1 --- /dev/null +++ b/.github/workflows/fossa.yml @@ -0,0 +1,16 @@ +name: FOSSA License Check + +on: + pull_request: + push: + branches: + - main + +jobs: + fossa-scan: + runs-on: [self-hosted, ubuntu-latest] + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v3 + - uses: fossas/fossa-action@ff70fe9fe17cbd2040648f1c45e8ec4e4884dcf3 # ratchet:fossas/fossa-action@main + with: + api-key: ${{secrets.FOSSA_API_KEY}} diff --git a/factbook.yaml b/factbook.yaml index 4aa57e3..574cc67 100644 --- a/factbook.yaml +++ b/factbook.yaml @@ -4,6 +4,7 @@ spec: type: library lifecycle: production owner: ae@mdsol.com + experience: enablement metadata: json_schema: https://github.com/mdsol/platform-standards/tree/main/schemas/v1alpha1.schema.json name: mauth-core @@ -15,3 +16,5 @@ metadata: people: - role: technical owner email: ykitamura@mdsol.com + annotations: + product: N/A From 3f80a6475eb078374598fcee80d323ad50ee9965 Mon Sep 17 00:00:00 2001 From: "Edward J. Jinotti" Date: Mon, 20 Jul 2026 18:18:31 -0400 Subject: [PATCH 4/5] cloud runners --- .github/workflows/fossa.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/fossa.yml b/.github/workflows/fossa.yml index 25310f1..3dc2149 100644 --- a/.github/workflows/fossa.yml +++ b/.github/workflows/fossa.yml @@ -8,7 +8,7 @@ on: jobs: fossa-scan: - runs-on: [self-hosted, ubuntu-latest] + runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v3 - uses: fossas/fossa-action@ff70fe9fe17cbd2040648f1c45e8ec4e4884dcf3 # ratchet:fossas/fossa-action@main From 337d5f63d3ad4c1ae0a041d93d5c7fe77de68b16 Mon Sep 17 00:00:00 2001 From: "Edward J. Jinotti" Date: Mon, 20 Jul 2026 18:26:28 -0400 Subject: [PATCH 5/5] drop fossa --- .github/workflows/fossa.yml | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 .github/workflows/fossa.yml diff --git a/.github/workflows/fossa.yml b/.github/workflows/fossa.yml deleted file mode 100644 index 3dc2149..0000000 --- a/.github/workflows/fossa.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: FOSSA License Check - -on: - pull_request: - push: - branches: - - main - -jobs: - fossa-scan: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # ratchet:actions/checkout@v3 - - uses: fossas/fossa-action@ff70fe9fe17cbd2040648f1c45e8ec4e4884dcf3 # ratchet:fossas/fossa-action@main - with: - api-key: ${{secrets.FOSSA_API_KEY}}