Skip to content

Commit

Permalink
RSA PSS signing (#71)
Browse files Browse the repository at this point in the history
* Factor out checks for `libcrux` conformance of RSA public keys
* RSA signing using `libcrux`
* Update documentation
* Update `certificate_verify` in case of RSA signature
* Add reminder for #72
* Reminder to rework randomness (#73)
* RSA tests should no longer panic
  • Loading branch information
jschneider-bensch authored Nov 21, 2023
1 parent ea98bb5 commit 5f4b527
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 36 deletions.
6 changes: 0 additions & 6 deletions integration_tests/tests/self_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use simple_https_client::tls13client;
use simple_https_server::tls13server;

#[test]
#[should_panic]
fn test_sha256_chacha20_poly1305_rsa_pss_rsa_sha256_x25519() {
self_test_algorithm(simple_https_client::SHA256_Chacha20Poly1305_RsaPssRsaSha256_X25519);
}
Expand All @@ -17,7 +16,6 @@ fn test_sha256_chacha20_poly1305_ecdsa_secp256r1_sha256_p256() {
self_test_algorithm(simple_https_client::SHA256_Chacha20Poly1305_EcdsaSecp256r1Sha256_P256);
}
#[test]
#[should_panic]
fn test_sha256_chacha20_poly1305_rsa_pss_rsa_sha256_p256() {
self_test_algorithm(simple_https_client::SHA256_Chacha20Poly1305_RsaPssRsaSha256_P256);
}
Expand All @@ -34,14 +32,12 @@ fn test_sha256_aes128_gcm_ecdsa_secp256r1_sha256_x25519() {
}
}
#[test]
#[should_panic]
fn test_sha256_aes128_gcm_rsa_pss_rsa_sha256_p256() {
if libcrux_platform::aes_ni_support() {
self_test_algorithm(simple_https_client::SHA256_Aes128Gcm_RsaPssRsaSha256_P256);
}
}
#[test]
#[should_panic]
fn test_sha256_aes128_gcm_rsa_pss_rsa_sha256_x25519() {
if libcrux_platform::aes_ni_support() {
self_test_algorithm(simple_https_client::SHA256_Aes128Gcm_RsaPssRsaSha256_X25519);
Expand All @@ -60,14 +56,12 @@ fn test_sha384_aes256_gcm_ecdsa_secp256r1_sha256_x25519() {
}
}
#[test]
#[should_panic]
fn test_sha384_aes256_gcm_rsa_pss_rsa_sha256_p256() {
if libcrux_platform::aes_ni_support() {
self_test_algorithm(simple_https_client::SHA384_Aes256Gcm_RsaPssRsaSha256_P256);
}
}
#[test]
#[should_panic]
fn test_sha384_aes256_gcm_rsa_pss_rsa_sha256_x25519() {
if libcrux_platform::aes_ni_support() {
self_test_algorithm(simple_https_client::SHA384_Aes256Gcm_RsaPssRsaSha256_X25519);
Expand Down
92 changes: 70 additions & 22 deletions src/tls13crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use libcrux::{
};

use crate::{
eq, tlserr, Bytes, Declassify, TLSError, CRYPTO_ERROR, INVALID_SIGNATURE, UNSUPPORTED_ALGORITHM,
eq, tls13cert, tlserr, Bytes, Declassify, TLSError, CRYPTO_ERROR, INVALID_SIGNATURE,
UNSUPPORTED_ALGORITHM,
};

pub type Random = Bytes; //was [U8;32]
Expand Down Expand Up @@ -226,7 +227,9 @@ pub enum SignatureScheme {

pub fn to_libcrux_sig_alg(a: &SignatureScheme) -> Result<signature::Algorithm, TLSError> {
match a {
SignatureScheme::RsaPssRsaSha256 => tlserr(UNSUPPORTED_ALGORITHM),
SignatureScheme::RsaPssRsaSha256 => Ok(signature::Algorithm::RsaPss(
signature::DigestAlgorithm::Sha256,
)),
SignatureScheme::ED25519 => Ok(signature::Algorithm::Ed25519),
SignatureScheme::EcdsaSecp256r1Sha256 => Ok(signature::Algorithm::EcDsaP256(
signature::DigestAlgorithm::Sha256,
Expand All @@ -237,15 +240,48 @@ pub fn to_libcrux_sig_alg(a: &SignatureScheme) -> Result<signature::Algorithm, T
pub fn sign(
alg: &SignatureScheme,
sk: &Bytes,
cert: &Bytes, // TODO: `cert` added to allow reconstructing full signing key for RSA-PSS. Rework this. (cf. issue #72)
input: &Bytes,
ent: Bytes,
ent: Bytes, // TODO: Rework handling of randomness, `libcrux` may want an `impl CryptoRng + RngCore` instead of raw bytes. (cf. issue #73)
) -> Result<Bytes, TLSError> {
let sig = signature::sign(
to_libcrux_sig_alg(alg)?,
&input.declassify(),
&sk.declassify(),
&mut rand::thread_rng(),
);
let sig = match alg {
SignatureScheme::EcdsaSecp256r1Sha256 | SignatureScheme::ED25519 => signature::sign(
to_libcrux_sig_alg(alg)?,
&input.declassify(),
&sk.declassify(),
&mut rand::thread_rng(),
),
SignatureScheme::RsaPssRsaSha256 => {
// salt must be same length as digest ouput length
let mut salt = [0u8; 32];
use rand::RngCore;
rand::thread_rng().fill_bytes(&mut salt);
let (cert_scheme, cert_slice) = tls13cert::verification_key_from_cert(cert)?;
if !matches!(cert_scheme, SignatureScheme::RsaPssRsaSha256) {
return tlserr(CRYPTO_ERROR); // XXX: Right error type?
}
let (pk_modulus, pk_exponent) = tls13cert::rsa_public_key(cert, cert_slice)?;

if !valid_rsa_exponent(pk_exponent.declassify()) {
return tlserr(UNSUPPORTED_ALGORITHM);
}

let key_size = supported_rsa_key_size(&pk_modulus)?;

let pk = RsaPssPublicKey::new(key_size, &pk_modulus.declassify()[1..])
.map_err(|_| CRYPTO_ERROR)?;

let sk = signature::rsa_pss::RsaPssPrivateKey::new(&pk, &sk.declassify())
.map_err(|_| CRYPTO_ERROR)?;

sk.sign(
signature::DigestAlgorithm::Sha256,
&salt,
&input.declassify(),
)
.map(signature::Signature::RsaPss)
}
};
match sig {
Ok(signature::Signature::Ed25519(sig)) => Ok(sig.as_bytes().into()),
Ok(signature::Signature::EcDsaP256(sig)) => {
Expand Down Expand Up @@ -292,21 +328,12 @@ pub fn verify(
}
}
(SignatureScheme::RsaPssRsaSha256, PublicVerificationKey::Rsa((n, e))) => {
let e = e.declassify();
if !(e.len() == 3 && e[0] == 0x1 && e[1] == 0x0 && e[2] == 0x1) {
// libcrux only supports `e = 3`
if !valid_rsa_exponent(e.declassify()) {
tlserr(UNSUPPORTED_ALGORITHM)
} else {
let key_size = match n.len() {
// The format includes an extra 0-byte in front to disambiguate from negative numbers
257 => RsaPssKeySize::N2048,
385 => RsaPssKeySize::N3072,
513 => RsaPssKeySize::N4096,
769 => RsaPssKeySize::N6144,
1025 => RsaPssKeySize::N8192,
_ => return tlserr(UNSUPPORTED_ALGORITHM),
};
let pk = RsaPssPublicKey::new(key_size, &n.declassify()[1..]).unwrap();
let key_size = supported_rsa_key_size(n)?;
let pk = RsaPssPublicKey::new(key_size, &n.declassify()[1..])
.map_err(|_| CRYPTO_ERROR)?;
let res = pk.verify(
signature::DigestAlgorithm::Sha256,
&sig.declassify().into(),
Expand All @@ -323,6 +350,27 @@ pub fn verify(
}
}

/// Determine if given modulus conforms to one of the key sizes supported by
/// `libcrux`.
fn supported_rsa_key_size(n: &Bytes) -> Result<RsaPssKeySize, u8> {
let key_size = match n.len() {
// The format includes an extra 0-byte in front to disambiguate from negative numbers
257 => RsaPssKeySize::N2048,
385 => RsaPssKeySize::N3072,
513 => RsaPssKeySize::N4096,
769 => RsaPssKeySize::N6144,
1025 => RsaPssKeySize::N8192,
_ => return tlserr(UNSUPPORTED_ALGORITHM),
};
Ok(key_size)
}

/// Determine if given public exponent is supported by `libcrux`, i.e. whether
/// `e == 0x010001`.
fn valid_rsa_exponent(e: Vec<u8>) -> bool {
e.len() == 3 && e[0] == 0x1 && e[1] == 0x0 && e[2] == 0x1
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum KemScheme {
X25519,
Expand Down
20 changes: 13 additions & 7 deletions src/tls13formats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -906,13 +906,19 @@ fn parse_ecdsa_signature(sig: Bytes) -> Result<Bytes, TLSError> {
}
}
pub fn certificate_verify(algs: &Algorithms, cv: &Bytes) -> Result<HandshakeData, TLSError> {
if cv.len() != 64 {
tlserr(parse_failed())
} else {
let sv = ecdsa_signature(cv)?;
let sig = signature_algorithm(algs)?.concat(&lbytes2(&sv)?);
Ok(handshake_message(HandshakeType::CertificateVerify, &sig)?)
}
let sv = match algs.2 {
SignatureScheme::RsaPssRsaSha256 => cv.clone(),
SignatureScheme::EcdsaSecp256r1Sha256 | SignatureScheme::ED25519 => {
if cv.len() != 64 {
return tlserr(parse_failed());
} else {
ecdsa_signature(cv)?
}
}
};

let sig = signature_algorithm(algs)?.concat(&lbytes2(&sv)?);
handshake_message(HandshakeType::CertificateVerify, &sig)
}

pub fn parse_certificate_verify(algs: &Algorithms, cv: &HandshakeData) -> Result<Bytes, TLSError> {
Expand Down
2 changes: 1 addition & 1 deletion src/tls13handshake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ fn get_server_signature(
let tx = transcript_add1(tx, &sc);
let th = get_transcript_hash(&tx)?;
let sigval = Bytes::from_slice(&PREFIX_SERVER_SIGNATURE).concat(&th);
let sig = sign(&sig_alg(&algs), &sigk, &sigval, ent)?;
let sig = sign(&sig_alg(&algs), &sigk, &cert, &sigval, ent)?;
let scv = certificate_verify(&algs, &sig)?;
let tx = transcript_add1(tx, &scv);
Ok((
Expand Down

0 comments on commit 5f4b527

Please sign in to comment.