Skip to content

Commit 7d48402

Browse files
committed
fix: re-enable mTLS on PGSQL/MYSQL
1 parent 166eb0d commit 7d48402

34 files changed

Lines changed: 849 additions & 175 deletions

File tree

.github/scripts/common.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -443,15 +443,15 @@ _run_workspace_tests() {
443443
local test_args="--nocapture"
444444
case "$KMS_TEST_DB" in
445445
postgresql)
446-
test_filter="tests::test_db_postgresql test_validate_with_certificates"
446+
test_filter="test_db_postgresql test_validate_with_certificates"
447447
test_args="$test_args --ignored"
448448
;;
449449
mysql)
450-
test_filter="tests::test_db_mysql test_validate_with_certificates"
450+
test_filter="test_db_mysql test_validate_with_certificates"
451451
test_args="$test_args --ignored"
452452
;;
453453
redis-findex)
454-
test_filter="tests::test_db_redis_with_findex test_validate_with_certificates"
454+
test_filter="test_db_redis_with_findex test_validate_with_certificates"
455455
test_args="$test_args --ignored"
456456
;;
457457
esac

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ repos:
171171
language: system
172172
types: [rust]
173173
pass_filenames: false
174+
stages: [manual]
174175

175176
- id: cargo-test-fips
176177
name: cargo test (sqlite fips)

Cargo.lock

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crate/cli/src/actions/kms/elliptic_curves/sign.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ impl SignAction {
4747
self.key_id.clone(),
4848
self.tags.clone(),
4949
self.output_file.clone(),
50+
None,
5051
self.digested,
5152
)
5253
.await

crate/cli/src/actions/kms/rsa/sign.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
use std::path::PathBuf;
22

33
use clap::Parser;
4+
use cosmian_kmip::{
5+
kmip_0::kmip_types::HashingAlgorithm,
6+
kmip_2_1::kmip_types::{CryptographicParameters, DigitalSignatureAlgorithm},
7+
};
48
use cosmian_kms_client::KmsClient;
59

610
use crate::{
@@ -37,12 +41,21 @@ pub struct SignAction {
3741

3842
impl SignAction {
3943
pub async fn run(&self, kms_rest_client: KmsClient) -> KmsCliResult<()> {
44+
// Explicitly set SHA-256 for RSA signing (FIPS-safe) instead of relying on
45+
// KMIP/OpenSSL defaults.
46+
let cryptographic_parameters = Some(CryptographicParameters {
47+
digital_signature_algorithm: Some(DigitalSignatureAlgorithm::RSASSAPSS),
48+
hashing_algorithm: Some(HashingAlgorithm::SHA256),
49+
mask_generator_hashing_algorithm: Some(HashingAlgorithm::SHA256),
50+
..CryptographicParameters::default()
51+
});
4052
run_sign(
4153
kms_rest_client,
4254
self.input_file.clone(),
4355
self.key_id.clone(),
4456
self.tags.clone(),
4557
self.output_file.clone(),
58+
cryptographic_parameters,
4659
self.digested,
4760
)
4861
.await

crate/cli/src/actions/kms/shared/sign.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use std::path::PathBuf;
22

3-
use cosmian_kmip::kmip_2_1::{kmip_operations::Sign, kmip_types::UniqueIdentifier};
3+
use cosmian_kmip::kmip_2_1::{
4+
kmip_operations::Sign,
5+
kmip_types::{CryptographicParameters, UniqueIdentifier},
6+
};
47
use cosmian_kms_client::{KmsClient, read_bytes_from_file, write_bytes_to_file};
58

69
use crate::{
@@ -14,6 +17,7 @@ pub(crate) async fn run_sign(
1417
key_id: Option<String>,
1518
tags: Option<Vec<String>>,
1619
output_file: Option<PathBuf>,
20+
cryptographic_parameters: Option<CryptographicParameters>,
1721
digested: bool,
1822
) -> KmsCliResult<()> {
1923
let data = read_bytes_from_file(&input_file)
@@ -24,7 +28,7 @@ pub(crate) async fn run_sign(
2428
let sign_request = if digested {
2529
Sign {
2630
unique_identifier: Some(UniqueIdentifier::TextString(id)),
27-
cryptographic_parameters: None,
31+
cryptographic_parameters,
2832
data: None,
2933
digested_data: Some(data),
3034
correlation_value: None,
@@ -34,7 +38,7 @@ pub(crate) async fn run_sign(
3438
} else {
3539
Sign {
3640
unique_identifier: Some(UniqueIdentifier::TextString(id)),
37-
cryptographic_parameters: None,
41+
cryptographic_parameters,
3842
data: Some(data.into()),
3943
digested_data: None,
4044
correlation_value: None,

crate/cli/src/tests/kms/digested.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use cosmian_kmip::kmip_2_1::{
55
kmip_types::{UniqueIdentifier, ValidityIndicator},
66
};
77
use cosmian_logger::log_init;
8-
// Curve no longer used since EcSignAction was removed
98
use sha2::Digest as Sha2Digest;
109
use tempfile::TempDir;
1110
use test_kms_server::start_default_test_kms_server;

crate/crypto/src/crypto/elliptic_curves/sign.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub fn ecdsa_sign(request: &Sign, private_key: &PKey<Private>) -> Result<Vec<u8>
4040
}
4141
};
4242

43-
// RFC6979 path for non-fips builds
43+
// RFC6979 deterministic ECDSA for P-256 and secp256k1 in non-fips builds
4444
#[cfg(feature = "non-fips")]
4545
{
4646
if let Ok(ec_key) = private_key.ec_key() {

crate/crypto/src/crypto/rsa/sign.rs

Lines changed: 130 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use openssl::{
1414

1515
use crate::{
1616
CryptoError, crypto::rsa::default_cryptographic_parameters, error::result::CryptoResult,
17+
openssl::hashing_algorithm_to_openssl_ref,
1718
};
1819

1920
/// Attempt to sign a digest using an RSA private key and the provided algorithm name.
@@ -84,43 +85,63 @@ pub fn sign_rsa_with_pkey(request: &Sign, private_key: &PKey<Private>) -> Crypto
8485
let (_algorithm, _padding, default_hash, digital_signature_algorithm) =
8586
default_cryptographic_parameters(request.cryptographic_parameters.as_ref());
8687

87-
// Determine effective message digest
88-
let digest = if let Some(cp) = request.cryptographic_parameters.as_ref() {
89-
if let Some(h) = &cp.hashing_algorithm {
90-
match h {
91-
KmipHash::SHA1 => MessageDigest::sha1(),
92-
KmipHash::SHA256 => MessageDigest::sha256(),
93-
KmipHash::SHA384 => MessageDigest::sha384(),
94-
KmipHash::SHA512 => MessageDigest::sha512(),
95-
KmipHash::SHA3256 => MessageDigest::sha3_256(),
96-
KmipHash::SHA3384 => MessageDigest::sha3_384(),
97-
KmipHash::SHA3512 => MessageDigest::sha3_512(),
98-
_ => {
99-
return Err(CryptoError::Default(
100-
"sign_rsa_with_pkey: hashing algorithm not supported".to_owned(),
101-
));
102-
}
103-
}
88+
// Determine effective hashing algorithm (KMIP) first, then map to OpenSSL.
89+
let mut effective_hash: KmipHash = if let Some(cp) = request.cryptographic_parameters.as_ref() {
90+
if let Some(h) = cp.hashing_algorithm {
91+
h
10492
} else {
10593
match digital_signature_algorithm {
10694
DigitalSignatureAlgorithm::RSASSAPSS
107-
| DigitalSignatureAlgorithm::SHA256WithRSAEncryption => MessageDigest::sha256(),
108-
DigitalSignatureAlgorithm::SHA384WithRSAEncryption => MessageDigest::sha384(),
109-
DigitalSignatureAlgorithm::SHA512WithRSAEncryption => MessageDigest::sha512(),
110-
DigitalSignatureAlgorithm::SHA3256WithRSAEncryption => MessageDigest::sha3_256(),
111-
DigitalSignatureAlgorithm::SHA3384WithRSAEncryption => MessageDigest::sha3_384(),
112-
DigitalSignatureAlgorithm::SHA3512WithRSAEncryption => MessageDigest::sha3_512(),
113-
_ => {
95+
| DigitalSignatureAlgorithm::SHA256WithRSAEncryption => KmipHash::SHA256,
96+
DigitalSignatureAlgorithm::SHA384WithRSAEncryption => KmipHash::SHA384,
97+
DigitalSignatureAlgorithm::SHA512WithRSAEncryption => KmipHash::SHA512,
98+
DigitalSignatureAlgorithm::SHA3256WithRSAEncryption => KmipHash::SHA3256,
99+
DigitalSignatureAlgorithm::SHA3384WithRSAEncryption => KmipHash::SHA3384,
100+
DigitalSignatureAlgorithm::SHA3512WithRSAEncryption => KmipHash::SHA3512,
101+
other => {
114102
return Err(CryptoError::Default(format!(
115-
"sign_rsa_with_pkey: not supported: {digital_signature_algorithm:?}"
103+
"sign_rsa_with_pkey: not supported: {other:?}"
116104
)));
117105
}
118106
}
119107
}
108+
} else if let Some(digested_data) = &request.digested_data {
109+
// When no cryptographic parameters are provided but we have digested data,
110+
// infer the digest algorithm from the size of the digest
111+
match digested_data.len() {
112+
20 => KmipHash::SHA1,
113+
32 => KmipHash::SHA256,
114+
48 => KmipHash::SHA384,
115+
64 => KmipHash::SHA512,
116+
_ => default_hash,
117+
}
120118
} else {
121-
map_kmip_hash_to_openssl(default_hash)
119+
default_hash
122120
};
123121

122+
// If the caller provided pre-digested data, prefer inferring the hash from the digest length.
123+
// This avoids accidentally selecting SHA-1 (disallowed in FIPS) when the digest is clearly
124+
// SHA-256/384/512.
125+
if let Some(digested_data) = &request.digested_data {
126+
effective_hash = match digested_data.len() {
127+
20 => KmipHash::SHA1,
128+
32 => KmipHash::SHA256,
129+
48 => KmipHash::SHA384,
130+
64 => KmipHash::SHA512,
131+
_ => effective_hash,
132+
};
133+
}
134+
135+
// OpenSSL FIPS provider forbids SHA-1 for RSA signing.
136+
#[cfg(not(feature = "non-fips"))]
137+
if effective_hash == KmipHash::SHA1 {
138+
return Err(CryptoError::Default(
139+
"RSA signing with SHA-1 is not supported in FIPS mode".to_owned(),
140+
));
141+
}
142+
143+
let digest = map_kmip_hash_to_openssl(effective_hash);
144+
124145
// RSASSA-PSS: pre-hash path when digested_data provided
125146
if digital_signature_algorithm == DigitalSignatureAlgorithm::RSASSAPSS
126147
&& request.digested_data.is_some()
@@ -131,24 +152,30 @@ pub fn sign_rsa_with_pkey(request: &Sign, private_key: &PKey<Private>) -> Crypto
131152
}
132153
let mut ctx = PkeyCtx::new(private_key)?;
133154
ctx.sign_init()?;
134-
ctx.set_rsa_padding(Padding::PKCS1_PSS)?;
155+
let mgf1_hash = request
156+
.cryptographic_parameters
157+
.as_ref()
158+
.and_then(|cp| cp.mask_generator_hashing_algorithm)
159+
.unwrap_or(effective_hash);
135160

136-
if let Some(cp) = request.cryptographic_parameters.as_ref() {
137-
if let Some(h) = cp.mask_generator_hashing_algorithm {
138-
let mgf1 = map_kmip_hash_to_openssl(h);
139-
#[allow(unsafe_code)]
140-
ctx.set_rsa_mgf1_md(unsafe { &*(mgf1.as_ptr().cast::<openssl::md::MdRef>()) })?;
141-
} else {
142-
#[allow(unsafe_code)]
143-
ctx.set_rsa_mgf1_md(unsafe { &*(digest.as_ptr().cast::<openssl::md::MdRef>()) })?;
144-
}
161+
#[cfg(not(feature = "non-fips"))]
162+
let mgf1_hash = if mgf1_hash == KmipHash::SHA1 {
163+
effective_hash
145164
} else {
146-
#[allow(unsafe_code)]
147-
ctx.set_rsa_mgf1_md(unsafe { &*(digest.as_ptr().cast::<openssl::md::MdRef>()) })?;
148-
}
149-
// Tell OpenSSL what the hash type is
150-
#[allow(unsafe_code)]
151-
ctx.set_signature_md(unsafe { &*(digest.as_ptr().cast::<openssl::md::MdRef>()) })?;
165+
mgf1_hash
166+
};
167+
168+
// OpenSSL FIPS provider forbids SHA-1 for RSA PSS MGF1.
169+
// KMIP says the default MGF1 hash is SHA-1 when omitted, so we must
170+
// override that default to match the signature hash (typically SHA-256)
171+
// for FIPS compatibility.
172+
// Set the signature digest first so OpenSSL doesn't initialize RSA-PSS
173+
// with SHA-1 defaults (disallowed in FIPS).
174+
ctx.set_signature_md(hashing_algorithm_to_openssl_ref(effective_hash)?)?;
175+
// Then select PSS padding; the digest is already configured.
176+
ctx.set_rsa_padding(Padding::PKCS1_PSS)?;
177+
// MGF1 digest is a PSS-only parameter, so set it after PSS is selected.
178+
ctx.set_rsa_mgf1_md(hashing_algorithm_to_openssl_ref(mgf1_hash)?)?;
152179

153180
let salt_len = request
154181
.cryptographic_parameters
@@ -162,19 +189,77 @@ pub fn sign_rsa_with_pkey(request: &Sign, private_key: &PKey<Private>) -> Crypto
162189
})?;
163190
buffer.extend_from_slice(digested_data);
164191
// First call: Pass None to get the required buffer size
165-
let required_len = ctx.sign(&buffer, None)?;
192+
let required_len = ctx.sign(&buffer, None).map_err(|e| {
193+
CryptoError::Default(format!(
194+
"rsa pss prehash sign init failed (hash={effective_hash:?}, mgf1={mgf1_hash:?}, payload_len={}): {e}",
195+
buffer.len()
196+
))
197+
})?;
166198
// Second call: Pass a buffer of the correct size
167199
let mut signature = vec![0_u8; required_len];
168-
ctx.sign(&buffer, Some(&mut signature))?;
200+
ctx.sign(&buffer, Some(&mut signature)).map_err(|e| {
201+
CryptoError::Default(format!(
202+
"rsa pss prehash sign failed (hash={effective_hash:?}, mgf1={mgf1_hash:?}, payload_len={}): {e}",
203+
buffer.len()
204+
))
205+
})?;
206+
return Ok(signature);
207+
}
208+
209+
// PKCS#1 v1.5: pre-hash path when digested_data provided
210+
// Use PkeyCtx for FIPS compatibility with pre-digested data
211+
if request.digested_data.is_some() {
212+
let digested_data = request
213+
.digested_data
214+
.as_ref()
215+
.ok_or_else(|| CryptoError::ObjectNotFound("Missing digested data".to_owned()))?;
216+
217+
let mut buffer = Vec::new();
218+
if let Some(corr) = &request.correlation_value {
219+
buffer.extend_from_slice(corr);
220+
}
221+
buffer.extend_from_slice(digested_data);
222+
223+
let mut ctx = PkeyCtx::new(private_key)?;
224+
ctx.sign_init()?;
225+
ctx.set_signature_md(hashing_algorithm_to_openssl_ref(effective_hash)?)?;
226+
// Tell OpenSSL what the hash type is for the pre-digested data.
227+
// Set it before padding so OpenSSL doesn't use SHA-1 defaults.
228+
ctx.set_rsa_padding(Padding::PKCS1)?;
229+
230+
// First call: Pass None to get the required buffer size
231+
let required_len = ctx.sign(&buffer, None).map_err(|e| {
232+
CryptoError::Default(format!(
233+
"rsa pkcs1 prehash sign init failed (hash={effective_hash:?}, payload_len={}): {e}",
234+
buffer.len()
235+
))
236+
})?;
237+
// Second call: Pass a buffer of the correct size
238+
let mut signature = vec![0_u8; required_len];
239+
ctx.sign(&buffer, Some(&mut signature)).map_err(|e| {
240+
CryptoError::Default(format!(
241+
"rsa pkcs1 prehash sign failed (hash={effective_hash:?}, payload_len={}): {e}",
242+
buffer.len()
243+
))
244+
})?;
169245
return Ok(signature);
170246
}
171247

248+
// Standard path for non-digested data
172249
let mut signer = Signer::new(digest, private_key)?;
173250
if DigitalSignatureAlgorithm::RSASSAPSS == digital_signature_algorithm {
174251
signer.set_rsa_padding(Padding::PKCS1_PSS)?;
175252
if let Some(cp) = request.cryptographic_parameters.as_ref() {
176253
if let Some(h) = cp.mask_generator_hashing_algorithm {
177-
let mgf1 = map_kmip_hash_to_openssl(h);
254+
let mgf1_hash = h;
255+
256+
#[cfg(not(feature = "non-fips"))]
257+
let mgf1_hash = if mgf1_hash == KmipHash::SHA1 {
258+
effective_hash
259+
} else {
260+
mgf1_hash
261+
};
262+
let mgf1 = map_kmip_hash_to_openssl(mgf1_hash);
178263
signer.set_rsa_mgf1_md(mgf1)?;
179264
} else {
180265
signer.set_rsa_mgf1_md(digest)?;

0 commit comments

Comments
 (0)