script: Implement import key operation of RSA-PSS (#41157)

Start adding RSA-PSS support to WebCrypto API.

This patch implements import key operation of RSA-PSS, with `rsa` crate.

Testing:
- Pass some WPT tests that were expected to fail.
- Some new FAIL expectations are added. They were skipped by WPT when
the import key operation of RSA-PSS had not been implemented, and
requires other not-yet-implemented operations to pass.

Fixes: #34362, and part of #41113

---------

Signed-off-by: Kingsley Yung <kingsley@kkoyung.dev>
This commit is contained in:
Kingsley Yung
2025-12-09 23:34:01 +08:00
committed by GitHub
parent 6de21e1fd5
commit 56d9e24bff
12 changed files with 1203 additions and 1011 deletions

72
Cargo.lock generated
View File

@@ -5054,6 +5054,9 @@ name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
dependencies = [
"spin",
]
[[package]]
name = "libc"
@@ -5827,6 +5830,23 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-bigint-dig"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7"
dependencies = [
"lazy_static",
"libm",
"num-integer",
"num-iter",
"num-traits",
"rand 0.8.5",
"serde",
"smallvec",
"zeroize",
]
[[package]]
name = "num-complex"
version = "0.2.4"
@@ -5863,6 +5883,17 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.2"
@@ -6722,6 +6753,17 @@ dependencies = [
"webrender_api",
]
[[package]]
name = "pkcs1"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
dependencies = [
"der",
"pkcs8",
"spki",
]
[[package]]
name = "pkcs8"
version = "0.10.2"
@@ -7356,6 +7398,28 @@ version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
[[package]]
name = "rsa"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88"
dependencies = [
"const-oid",
"digest",
"num-bigint-dig",
"num-integer",
"num-traits",
"pkcs1",
"pkcs8",
"rand_core 0.6.4",
"sha1",
"sha2",
"signature",
"spki",
"subtle",
"zeroize",
]
[[package]]
name = "rusqlite"
version = "0.37.0"
@@ -7633,6 +7697,7 @@ dependencies = [
"net_traits",
"nom 8.0.0",
"nom-rfc8288",
"num-bigint-dig",
"num-traits",
"num_cpus",
"p256",
@@ -7647,6 +7712,7 @@ dependencies = [
"rand 0.9.2",
"range",
"regex",
"rsa",
"rustc-hash 2.1.1",
"script_bindings",
"script_traits",
@@ -8549,6 +8615,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b72d540d5c565dbe1f891d7e21ceb21d2649508306782f1066989fccb0b363d3"
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "spirv"
version = "0.3.0+sdk-1.3.268.0"

View File

@@ -116,6 +116,7 @@ net_traits = { path = "components/shared/net" }
nix = "0.30"
nom = "8.0.0"
nom-rfc8288 = "0.4.0"
num-bigint-dig = "0.8"
num-traits = "0.2"
num_cpus = "1.17.0"
openxr = "0.19"
@@ -135,6 +136,7 @@ rayon = "1"
read-fonts = "0.35.0"
regex = "1.12"
resvg = "0.45.0"
rsa = { version = "0.9.9", features = ["sha1", "sha2"] }
rustc-hash = "2.1.1"
rustls = { version = "0.23", default-features = false, features = ["logging", "std", "tls12"] }
rustls-pki-types = "1.13"

View File

@@ -102,6 +102,7 @@ mime_guess = { workspace = true }
net_traits = { workspace = true }
nom = { workspace = true }
nom-rfc8288 = { workspace = true }
num-bigint-dig = { workspace = true }
num-traits = { workspace = true }
num_cpus = { workspace = true }
p256 = { workspace = true }
@@ -116,6 +117,7 @@ profile_traits = { workspace = true }
rand = { workspace = true }
range = { path = "../range" }
regex = { workspace = true }
rsa = { workspace = true }
rustc-hash = { workspace = true }
script_bindings = { path = "../script_bindings" }
script_traits = { workspace = true }

View File

@@ -27,6 +27,8 @@ pub(crate) enum CryptoKeyOrCryptoKeyPair {
/// The underlying cryptographic data this key represents
pub(crate) enum Handle {
RsaPrivateKey(rsa::RsaPrivateKey),
RsaPublicKey(rsa::RsaPublicKey),
P256PrivateKey(p256::SecretKey),
P384PrivateKey(p384::SecretKey),
P521PrivateKey(p521::SecretKey),
@@ -214,6 +216,8 @@ impl Handle {
impl MallocSizeOf for Handle {
fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
match self {
Handle::RsaPrivateKey(private_key) => private_key.size_of(ops),
Handle::RsaPublicKey(public_key) => public_key.size_of(ops),
Handle::P256PrivateKey(private_key) => private_key.size_of(ops),
Handle::P384PrivateKey(private_key) => private_key.size_of(ops),
Handle::P521PrivateKey(private_key) => private_key.size_of(ops),

View File

@@ -12,6 +12,7 @@ mod ed25519_operation;
mod hkdf_operation;
mod hmac_operation;
mod pbkdf2_operation;
mod rsa_pss_operation;
mod sha3_operation;
mod sha_operation;
mod x25519_operation;
@@ -37,7 +38,8 @@ use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{
AesKeyGenParams, Algorithm, AlgorithmIdentifier, Argon2Params, CShakeParams, EcKeyAlgorithm,
EcKeyGenParams, EcKeyImportParams, EcdhKeyDeriveParams, EcdsaParams, HkdfParams,
HmacImportParams, HmacKeyAlgorithm, HmacKeyGenParams, JsonWebKey, KeyAlgorithm, KeyFormat,
Pbkdf2Params, RsaOtherPrimesInfo, SubtleCryptoMethods,
Pbkdf2Params, RsaHashedImportParams, RsaHashedKeyAlgorithm, RsaKeyAlgorithm,
RsaOtherPrimesInfo, SubtleCryptoMethods,
};
use crate::dom::bindings::codegen::UnionTypes::{
ArrayBufferViewOrArrayBuffer, ArrayBufferViewOrArrayBufferOrJsonWebKey, ObjectOrString,
@@ -1680,6 +1682,73 @@ impl SafeToJSValConvertible for SubtleKeyAlgorithm {
}
}
/// <https://w3c.github.io/webcrypto/#dfn-RsaHashedKeyAlgorithm>
#[derive(Clone, MallocSizeOf)]
pub(crate) struct SubtleRsaHashedKeyAlgorithm {
/// <https://w3c.github.io/webcrypto/#dom-keyalgorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-RsaKeyAlgorithm-modulusLength>
modulus_length: u32,
/// <https://w3c.github.io/webcrypto/#dfn-RsaKeyAlgorithm-publicExponent>
public_exponent: Vec<u8>,
/// <https://w3c.github.io/webcrypto/#dfn-RsaHashedKeyAlgorithm-hash>
hash: Box<NormalizedAlgorithm>,
}
impl SafeToJSValConvertible for SubtleRsaHashedKeyAlgorithm {
fn safe_to_jsval(&self, cx: JSContext, rval: MutableHandleValue, can_gc: CanGc) {
rooted!(in(*cx) let mut js_object = ptr::null_mut::<JSObject>());
let public_exponent =
create_buffer_source(cx, &self.public_exponent, js_object.handle_mut(), can_gc)
.expect("Fail to convert publicExponent to Uint8Array");
let key_algorithm = KeyAlgorithm {
name: self.name.clone().into(),
};
let rsa_key_algorithm = RootedTraceableBox::new(RsaKeyAlgorithm {
parent: key_algorithm,
modulusLength: self.modulus_length,
publicExponent: public_exponent,
});
let rsa_hashed_key_algorithm = RootedTraceableBox::new(RsaHashedKeyAlgorithm {
parent: rsa_key_algorithm,
hash: KeyAlgorithm {
name: self.hash.name().into(),
},
});
rsa_hashed_key_algorithm.safe_to_jsval(cx, rval, can_gc);
}
}
/// <https://w3c.github.io/webcrypto/#dfn-RsaHashedImportParams>
#[derive(Clone, MallocSizeOf)]
struct SubtleRsaHashedImportParams {
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
name: String,
/// <https://w3c.github.io/webcrypto/#dfn-RsaHashedImportParams-hash>
hash: Box<NormalizedAlgorithm>,
}
impl TryFrom<RootedTraceableBox<RsaHashedImportParams>> for SubtleRsaHashedImportParams {
type Error = Error;
fn try_from(value: RootedTraceableBox<RsaHashedImportParams>) -> Result<Self, Self::Error> {
let cx = GlobalScope::get_cx();
Ok(SubtleRsaHashedImportParams {
name: value.parent.name.to_string(),
hash: Box::new(normalize_algorithm(
cx,
&Operation::Digest,
&value.hash,
CanGc::note(),
)?),
})
}
}
/// <https://w3c.github.io/webcrypto/#dfn-EcdsaParams>
#[derive(Clone, Debug, MallocSizeOf)]
struct SubtleEcdsaParams {
@@ -2250,10 +2319,11 @@ enum ExportedKey {
/// Union type of KeyAlgorithm and IDL dictionary types derived from it. Note that we actually use
/// our "subtle" structs of the corresponding IDL dictionary types so that they can be easily
/// passed to another threads.
#[derive(Clone, Debug, MallocSizeOf)]
#[derive(Clone, MallocSizeOf)]
#[expect(clippy::enum_variant_names)]
pub(crate) enum KeyAlgorithmAndDerivatives {
KeyAlgorithm(SubtleKeyAlgorithm),
RsaHashedKeyAlgorithm(SubtleRsaHashedKeyAlgorithm),
EcKeyAlgorithm(SubtleEcKeyAlgorithm),
AesKeyAlgorithm(SubtleAesKeyAlgorithm),
HmacKeyAlgorithm(SubtleHmacKeyAlgorithm),
@@ -2263,6 +2333,7 @@ impl KeyAlgorithmAndDerivatives {
fn name(&self) -> &str {
match self {
KeyAlgorithmAndDerivatives::KeyAlgorithm(algo) => &algo.name,
KeyAlgorithmAndDerivatives::RsaHashedKeyAlgorithm(algo) => &algo.name,
KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algo) => &algo.name,
KeyAlgorithmAndDerivatives::AesKeyAlgorithm(algo) => &algo.name,
KeyAlgorithmAndDerivatives::HmacKeyAlgorithm(algo) => &algo.name,
@@ -2282,6 +2353,9 @@ impl SafeToJSValConvertible for KeyAlgorithmAndDerivatives {
fn safe_to_jsval(&self, cx: JSContext, rval: MutableHandleValue, can_gc: CanGc) {
match self {
KeyAlgorithmAndDerivatives::KeyAlgorithm(algo) => algo.safe_to_jsval(cx, rval, can_gc),
KeyAlgorithmAndDerivatives::RsaHashedKeyAlgorithm(algo) => {
algo.safe_to_jsval(cx, rval, can_gc)
},
KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algo) => {
algo.safe_to_jsval(cx, rval, can_gc)
},
@@ -2452,6 +2526,7 @@ impl JsonWebKeyExt for JsonWebKey {
#[derive(Clone, MallocSizeOf)]
enum NormalizedAlgorithm {
Algorithm(SubtleAlgorithm),
RsaHashedImportParams(SubtleRsaHashedImportParams),
EcdsaParams(SubtleEcdsaParams),
EcKeyGenParams(SubtleEcKeyGenParams),
EcKeyImportParams(SubtleEcKeyImportParams),
@@ -2554,6 +2629,15 @@ fn normalize_algorithm(
// NOTE: Step 10.1.3 is done by the `From` and `TryFrom` trait implementation of
// "subtle" binding structs.
let normalized_algorithm = match (alg_name, op) {
// <https://w3c.github.io/webcrypto/#rsa-pss-registration>
(ALG_RSA_PSS, Operation::ImportKey) => {
let mut params = dictionary_from_jsval::<
RootedTraceableBox<RsaHashedImportParams>,
>(cx, value.handle(), can_gc)?;
params.parent.name = DOMString::from(alg_name);
NormalizedAlgorithm::RsaHashedImportParams(params.try_into()?)
},
// <https://w3c.github.io/webcrypto/#ecdsa-registration>
(ALG_ECDSA, Operation::Sign) => {
let mut params = dictionary_from_jsval::<RootedTraceableBox<EcdsaParams>>(
@@ -3086,6 +3170,7 @@ impl NormalizedAlgorithm {
fn name(&self) -> &str {
match self {
NormalizedAlgorithm::Algorithm(algo) => &algo.name,
NormalizedAlgorithm::RsaHashedImportParams(algo) => &algo.name,
NormalizedAlgorithm::EcdsaParams(algo) => &algo.name,
NormalizedAlgorithm::EcKeyGenParams(algo) => &algo.name,
NormalizedAlgorithm::EcKeyImportParams(algo) => &algo.name,
@@ -3291,6 +3376,17 @@ impl NormalizedAlgorithm {
can_gc: CanGc,
) -> Result<DomRoot<CryptoKey>, Error> {
match (self.name(), self) {
(ALG_RSA_PSS, NormalizedAlgorithm::RsaHashedImportParams(algo)) => {
rsa_pss_operation::import_key(
global,
algo,
format,
key_data,
extractable,
usages,
can_gc,
)
},
(ALG_ECDSA, NormalizedAlgorithm::EcKeyImportParams(algo)) => {
ecdsa_operation::import_key(
global,

View File

@@ -0,0 +1,466 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use base64ct::{Base64UrlUnpadded, Encoding};
use pkcs8::der::asn1::BitString;
use pkcs8::der::{AnyRef, Decode};
use pkcs8::{PrivateKeyInfo, SubjectPublicKeyInfo};
use rsa::pkcs1::{self, DecodeRsaPrivateKey};
use rsa::traits::PublicKeyParts;
use rsa::{BigUint, RsaPrivateKey, RsaPublicKey};
use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{KeyType, KeyUsage};
use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{
AlgorithmIdentifier, JsonWebKey, KeyFormat,
};
use crate::dom::bindings::error::Error;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::cryptokey::{CryptoKey, Handle};
use crate::dom::globalscope::GlobalScope;
use crate::dom::subtlecrypto::{
ALG_RSA_PSS, ALG_SHA1, ALG_SHA256, ALG_SHA384, ALG_SHA512, JsonWebKeyExt,
KeyAlgorithmAndDerivatives, Operation, SubtleRsaHashedImportParams,
SubtleRsaHashedKeyAlgorithm, normalize_algorithm,
};
use crate::script_runtime::CanGc;
/// <https://w3c.github.io/webcrypto/#rsa-pss-operations-import-key>
pub(crate) fn import_key(
global: &GlobalScope,
normalized_algorithm: &SubtleRsaHashedImportParams,
format: KeyFormat,
key_data: &[u8],
extractable: bool,
usages: Vec<KeyUsage>,
can_gc: CanGc,
) -> Result<DomRoot<CryptoKey>, Error> {
// Step 1. Let keyData be the key data to be imported.
// Step 2.
let (key_handle, key_type) = match format {
// If format is "spki":
KeyFormat::Spki => {
// Step 2.1. If usages contains an entry which is not "verify" then throw a
// SyntaxError.
if usages.iter().any(|usage| *usage != KeyUsage::Verify) {
return Err(Error::Syntax(Some(
"Usages contains an entry which is not \"verify\"".to_string(),
)));
}
// Step 2.2. Let spki be the result of running the parse a subjectPublicKeyInfo
// algorithm over keyData.
// Step 2.3. If an error occurred while parsing, then throw a DataError.
let spki =
SubjectPublicKeyInfo::<AnyRef, BitString>::from_der(key_data).map_err(|_| {
Error::Data(Some(
"Fail to parse SubjectPublicKeyInfo over keyData".to_string(),
))
})?;
// Step 2.4. If the algorithm object identifier field of the algorithm
// AlgorithmIdentifier field of spki is not equal to the rsaEncryption object
// identifier defined in [RFC3447], then throw a DataError.
if spki.algorithm.oid != pkcs1::ALGORITHM_OID {
return Err(Error::Data(Some(
"Algorithm object identifier of spki is not an rsaEncryption".to_string(),
)));
}
// Step 2.5. Let publicKey be the result of performing the parse an ASN.1 structure
// algorithm, with data as the subjectPublicKeyInfo field of spki, structure as the
// RSAPublicKey structure specified in Section A.1.1 of [RFC3447], and exactData set to
// true.
// Step 2.6. If an error occurred while parsing, or it can be determined that publicKey
// is not a valid public key according to [RFC3447], then throw a DataError.
let pkcs1_bytes = spki.subject_public_key.as_bytes().ok_or(Error::Data(Some(
"Fail to parse byte sequence over SubjectPublicKey field of spki".to_string(),
)))?;
let rsa_public_key_structure =
pkcs1::RsaPublicKey::try_from(pkcs1_bytes).map_err(|_| {
Error::Data(Some(
"SubjectPublicKey field of spki is not an RSAPublicKey structure"
.to_string(),
))
})?;
let n = BigUint::from_bytes_be(rsa_public_key_structure.modulus.as_bytes());
let e = BigUint::from_bytes_be(rsa_public_key_structure.public_exponent.as_bytes());
let public_key = RsaPublicKey::new(n, e).map_err(|_| {
Error::Data(Some(
"Fail to construct RSA public key from modulus and public exponent".to_string(),
))
})?;
// Step 2.7. Let key be a new CryptoKey that represents the RSA public key identified
// by publicKey.
// Step 2.8. Set the [[type]] internal slot of key to "public"
// NOTE: Done in Step 3-8.
let key_handle = Handle::RsaPublicKey(public_key);
let key_type = KeyType::Public;
(key_handle, key_type)
},
// If format is "pkcs8":
KeyFormat::Pkcs8 => {
// Step 2.1. If usages contains an entry which is not "sign" then throw a SyntaxError.
if usages.iter().any(|usage| *usage != KeyUsage::Sign) {
return Err(Error::Syntax(Some(
"Usages contains an entry which is not \"sign\"".to_string(),
)));
}
// Step 2.2. Let privateKeyInfo be the result of running the parse a privateKeyInfo
// algorithm over keyData.
// Step 2.3. If an error occurred while parsing, then throw a DataError.
let private_key_info = PrivateKeyInfo::from_der(key_data).map_err(|_| {
Error::Data(Some(
"Fail to parse PrivateKeyInfo over keyData".to_string(),
))
})?;
// Step 2.4. If the algorithm object identifier field of the privateKeyAlgorithm
// PrivateKeyAlgorithm field of privateKeyInfo is not equal to the rsaEncryption object
// identifier defined in [RFC3447], then throw a DataError.
if private_key_info.algorithm.oid != pkcs1::ALGORITHM_OID {
return Err(Error::Data(Some(
"Algorithm object identifier of PrivateKeyInfo is not an rsaEncryption"
.to_string(),
)));
}
// Step 2.5. Let rsaPrivateKey be the result of performing the parse an ASN.1 structure
// algorithm, with data as the privateKey field of privateKeyInfo, structure as the
// RSAPrivateKey structure specified in Section A.1.2 of [RFC3447], and exactData set
// to true.
// Step 2.6. If an error occurred while parsing, or if rsaPrivateKey is not a valid RSA
// private key according to [RFC3447], then throw a DataError.
let rsa_private_key = RsaPrivateKey::from_pkcs1_der(private_key_info.private_key)
.map_err(|_| {
Error::Data(Some(
"PrivateKey field of PrivateKeyInfo is not an RSAPrivateKey structure"
.to_string(),
))
})?;
// Step 2.7. Let key be a new CryptoKey that represents the RSA private key identified
// by rsaPrivateKey.
// Step 2.8. Set the [[type]] internal slot of key to "private"
// NOTE: Done in Step 3-8.
let key_handle = Handle::RsaPrivateKey(rsa_private_key);
let key_type = KeyType::Private;
(key_handle, key_type)
},
// If format is "jwk":
KeyFormat::Jwk => {
// Step 2.1.
// If keyData is a JsonWebKey dictionary:
// Let jwk equal keyData.
// Otherwise:
// Throw a DataError.
let cx = GlobalScope::get_cx();
let jwk = JsonWebKey::parse(cx, key_data)?;
// Step 2.2. If the d field of jwk is present and usages contains an entry which is not
// "sign", or, if the d field of jwk is not present and usages contains an entry which
// is not "verify" then throw a SyntaxError.
if jwk.d.is_some() && usages.iter().any(|usage| *usage != KeyUsage::Sign) {
return Err(Error::Syntax(Some(
"The d field of jwk is present and usages contains an entry which is not \"sign\""
.to_string()
)));
}
if jwk.d.is_none() && usages.iter().any(|usage| *usage != KeyUsage::Verify) {
return Err(Error::Syntax(Some(
"The d field of jwk is not present and usages contains an entry which is not \"verify\""
.to_string()
)));
}
// Step 2.3. If the kty field of jwk is not a case-sensitive string match to "RSA",
// then throw a DataError.
if jwk.kty.as_ref().is_none_or(|kty| kty != "RSA") {
return Err(Error::Data(Some(
"The kty field of jwk is not a case-sensitive string match to \"RSA\""
.to_string(),
)));
}
// Step 2.4. If usages is non-empty and the use field of jwk is present and is not a
// case-sensitive string match to "sig", then throw a DataError.
if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "sig") {
return Err(Error::Data(Some(
"Usages is non-empty and the use field of jwk is present and \
is not a case-sensitive string match to \"sig\""
.to_string(),
)));
}
// Step 2.5. If the key_ops field of jwk is present, and is invalid according to the
// requirements of JSON Web Key [JWK] or does not contain all of the specified usages
// values, then throw a DataError.
jwk.check_key_ops(&usages)?;
// Step 2.6. If the ext field of jwk is present and has the value false and extractable
// is true, then throw a DataError.
if jwk.ext.is_some_and(|ext| !ext) && extractable {
return Err(Error::Data(Some(
"The ext field of jwk is present and \
has the value false and extractable is true"
.to_string(),
)));
}
// Step 2.7.
// If the alg field of jwk is not present:
// Let hash be undefined.
// If the alg field is equal to the string "PS1":
// Let hash be the string "SHA-1".
// If the alg field is equal to the string "PS256":
// Let hash be the string "SHA-256".
// If the alg field is equal to the string "PS384":
// Let hash be the string "SHA-384".
// If the alg field is equal to the string "PS512":
// Let hash be the string "SHA-512".
// Otherwise:
// Perform any key import steps defined by other applicable specifications, passing
// format, jwk and obtaining hash.
// If an error occurred or there are no applicable specifications, throw a DataError.
let hash = match jwk.alg {
None => None,
Some(alg) => match &*alg.str() {
"PS1" => Some(ALG_SHA1),
"PS256" => Some(ALG_SHA256),
"PS384" => Some(ALG_SHA384),
"PS512" => Some(ALG_SHA512),
_ => None,
},
};
// Step 2.8. If hash is not undefined:
if let Some(hash) = hash {
// Step 2.8.1. Let normalizedHash be the result of normalize an algorithm with alg
// set to hash and op set to digest.
let normalized_hash = normalize_algorithm(
cx,
&Operation::Digest,
&AlgorithmIdentifier::String(DOMString::from(hash)),
can_gc,
)?;
// Step 2.8.2. If normalizedHash is not equal to the hash member of
// normalizedAlgorithm, throw a DataError.
if normalized_hash.name() != normalized_algorithm.hash.name() {
return Err(Error::Data(Some(
"The normalizedHash is not equal to the hash member of normalizedAlgorithm"
.to_string(),
)));
}
}
// Step 2.9.
match jwk.d {
// If the d field of jwk is present:
Some(d) => {
// Step 2.9.1. If jwk does not meet the requirements of Section 6.3.2 of JSON
// Web Algorithms [JWA], then throw a DataError.
let n = Base64UrlUnpadded::decode_vec(
&jwk.n
.ok_or(Error::Data(Some(
"The n field does not exist in jwk".to_string(),
)))?
.str(),
)
.map_err(|_| Error::Data(Some("Fail to decode n field of jwk".to_string())))?;
let e = Base64UrlUnpadded::decode_vec(
&jwk.e
.ok_or(Error::Data(Some(
"The e field does not exist in jwk".to_string(),
)))?
.str(),
)
.map_err(|_| Error::Data(Some("Fail to decode e field of jwk".to_string())))?;
let d = Base64UrlUnpadded::decode_vec(&d.str()).map_err(|_| {
Error::Data(Some("Fail to decode d field of jwk".to_string()))
})?;
let p = jwk
.p
.map(|p| Base64UrlUnpadded::decode_vec(&p.str()))
.transpose()
.map_err(|_| {
Error::Data(Some("Fail to decode p field of jwk".to_string()))
})?;
let q = jwk
.q
.map(|q| Base64UrlUnpadded::decode_vec(&q.str()))
.transpose()
.map_err(|_| {
Error::Data(Some("Fail to decode q field of jwk".to_string()))
})?;
let dp = jwk
.dp
.map(|dp| Base64UrlUnpadded::decode_vec(&dp.str()))
.transpose()
.map_err(|_| {
Error::Data(Some("Fail to decode dp field of jwk".to_string()))
})?;
let dq = jwk
.dq
.map(|dq| Base64UrlUnpadded::decode_vec(&dq.str()))
.transpose()
.map_err(|_| {
Error::Data(Some("Fail to decode dq field of jwk".to_string()))
})?;
let qi = jwk
.qi
.map(|qi| Base64UrlUnpadded::decode_vec(&qi.str()))
.transpose()
.map_err(|_| {
Error::Data(Some("Fail to decode qi field of jwk".to_string()))
})?;
let mut primes = match (p, q, dp, dq, qi) {
(Some(p), Some(q), Some(_dp), Some(_dq), Some(_qi)) => vec![p, q],
(None, None, None, None, None) => Vec::new(),
_ => return Err(Error::Data(Some(
"The p, q, dp, dq, qi fields of jwk must be either all-present or all-absent"
.to_string()
))),
};
if let Some(oth) = jwk.oth {
if primes.is_empty() {
return Err(Error::Data(Some(
"The oth field exists in jwk but one of p, q, dp, dq, qi is missing".to_string()
)));
}
for other_prime in oth {
let r = Base64UrlUnpadded::decode_vec(
&other_prime
.r
.ok_or(Error::Data(Some(
"The e field does not exist in oth field of jwk"
.to_string(),
)))?
.str(),
)
.map_err(|_| {
Error::Data(Some(
"Fail to decode r field of oth field of jwk".to_string(),
))
})?;
primes.push(r);
}
}
// Step 2.9.2. Let privateKey represents the RSA private key identified by
// interpreting jwk according to Section 6.3.2 of JSON Web Algorithms [JWA].
// Step 2.9.3. If privateKey is not a valid RSA private key according to
// [RFC3447], then throw a DataError.
let private_key = RsaPrivateKey::from_components(
BigUint::from_bytes_be(&n),
BigUint::from_bytes_be(&e),
BigUint::from_bytes_be(&d),
primes
.into_iter()
.map(|prime| BigUint::from_bytes_be(&prime))
.collect(),
)
.map_err(|_| {
Error::Data(Some(
"Fail to construct RSA private key from values in jwk".to_string(),
))
})?;
// Step 2.9.4. Let key be a new CryptoKey object that represents privateKey.
// Step 2.9.5. Set the [[type]] internal slot of key to "private"
// NOTE: Done in Step 3-8.
let key_handle = Handle::RsaPrivateKey(private_key);
let key_type = KeyType::Private;
(key_handle, key_type)
},
// Otherwise:
None => {
// Step 2.9.1. If jwk does not meet the requirements of Section 6.3.1 of JSON
// Web Algorithms [JWA], then throw a DataError.
let n = Base64UrlUnpadded::decode_vec(
&jwk.n
.ok_or(Error::Data(Some(
"The n field does not exist in jwk".to_string(),
)))?
.str(),
)
.map_err(|_| Error::Data(Some("Fail to decode n field of jwk".to_string())))?;
let e = Base64UrlUnpadded::decode_vec(
&jwk.e
.ok_or(Error::Data(Some(
"The e field does not exist in jwk".to_string(),
)))?
.str(),
)
.map_err(|_| Error::Data(Some("Fail to decode e field of jwk".to_string())))?;
// Step 2.9.2. Let publicKey represent the RSA public key identified by
// interpreting jwk according to Section 6.3.1 of JSON Web Algorithms [JWA].
// Step 2.9.3. If publicKey can be determined to not be a valid RSA public key
// according to [RFC3447], then throw a DataError.
let public_key =
RsaPublicKey::new(BigUint::from_bytes_be(&n), BigUint::from_bytes_be(&e))
.map_err(|_| {
Error::Data(Some(
"Fail to construct RSA public key from values in jwk".to_string(),
))
})?;
// Step 2.9.4. Let key be a new CryptoKey representing publicKey.
// Step 2.9.5. Set the [[type]] internal slot of key to "public"
// NOTE: Done in Step 3-8.
let key_handle = Handle::RsaPublicKey(public_key);
let key_type = KeyType::Public;
(key_handle, key_type)
},
}
},
// Otherwise:
_ => {
// throw a NotSupportedError.
return Err(Error::NotSupported(Some(
"Unsupported import key format for RSA key".to_string(),
)));
},
};
// Step 3. Let algorithm be a new RsaHashedKeyAlgorithm dictionary.
// Step 4. Set the name attribute of algorithm to "RSA-PSS"
// Step 5. Set the modulusLength attribute of algorithm to the length, in bits, of the RSA
// public modulus.
// Step 6. Set the publicExponent attribute of algorithm to the BigInteger representation of
// the RSA public exponent.
// Step 7. Set the hash attribute of algorithm to the hash member of normalizedAlgorithm.
// Step 8. Set the [[algorithm]] internal slot of key to algorithm
let (modulus_length, public_exponent) = match &key_handle {
Handle::RsaPrivateKey(private_key) => {
(private_key.size() as u32 * 8, private_key.e().to_bytes_be())
},
Handle::RsaPublicKey(public_key) => {
(public_key.size() as u32 * 8, public_key.e().to_bytes_be())
},
_ => unreachable!(),
};
let algorithm = SubtleRsaHashedKeyAlgorithm {
name: ALG_RSA_PSS.to_string(),
modulus_length,
public_exponent,
hash: normalized_algorithm.hash.clone(),
};
let key = CryptoKey::new(
global,
key_type,
extractable,
KeyAlgorithmAndDerivatives::RsaHashedKeyAlgorithm(algorithm),
usages,
key_handle,
can_gc,
);
// Step 9. Return key.
Ok(key)
}

View File

@@ -20,7 +20,10 @@ pub(crate) mod base {
#[allow(unused_imports)]
pub(crate) use js::realm::{AutoRealm, CurrentRealm};
pub(crate) use js::rust::wrappers::Call;
pub(crate) use js::rust::{HandleObject, HandleValue, MutableHandleObject, MutableHandleValue};
pub(crate) use js::rust::{
CustomTrace, HandleObject, HandleValue, MutableHandleObject, MutableHandleValue,
};
pub(crate) use js::typedarray;
pub(crate) use js::typedarray::{
ArrayBuffer, ArrayBufferView, Float32Array, Float64Array, Uint8Array, Uint8ClampedArray,
};

View File

@@ -56,6 +56,10 @@ interface SubtleCrypto {
sequence<KeyUsage> keyUsages );
};
// https://w3c.github.io/webcrypto/#big-integer
typedef Uint8Array BigInteger;
// https://w3c.github.io/webcrypto/#algorithm-dictionary
typedef (object or DOMString) AlgorithmIdentifier;
@@ -72,6 +76,25 @@ dictionary KeyAlgorithm {
required DOMString name;
};
// https://w3c.github.io/webcrypto/#RsaKeyAlgorithm-dictionary
dictionary RsaKeyAlgorithm : KeyAlgorithm {
required unsigned long modulusLength;
required BigInteger publicExponent;
};
// https://w3c.github.io/webcrypto/#RsaHashedKeyAlgorithm-dictionary
dictionary RsaHashedKeyAlgorithm : RsaKeyAlgorithm {
required KeyAlgorithm hash;
};
// https://w3c.github.io/webcrypto/#RsaHashedImportParams-dictionary
dictionary RsaHashedImportParams : Algorithm {
required HashAlgorithmIdentifier hash;
};
// https://w3c.github.io/webcrypto/#EcdsaParams-dictionary
dictionary EcdsaParams : Algorithm {
required HashAlgorithmIdentifier hash;

View File

@@ -24,6 +24,10 @@ ignore = [
"RUSTSEC-2025-0098",
# The crate `unic-ucd-ident` is unmaintained.
"RUSTSEC-2025-0100",
# The crate `rsa` is vulnerable to Marvin Attack that leaks
# cryptographic secret via side channel. Wait for a patch in stable
# release version from upstream.
"RUSTSEC-2023-0071",
]
# This section is considered when running `cargo deny check licenses`

File diff suppressed because it is too large Load Diff

View File

@@ -131,6 +131,30 @@
[importVectorKeys step: RSASSA-PKCS1-v1_5 with SHA-512 verification failure with altered plaintext]
expected: FAIL
[RSASSA-PKCS1-v1_5 with SHA-1 signing with wrong algorithm name]
expected: FAIL
[RSASSA-PKCS1-v1_5 with SHA-256 signing with wrong algorithm name]
expected: FAIL
[RSASSA-PKCS1-v1_5 with SHA-384 signing with wrong algorithm name]
expected: FAIL
[RSASSA-PKCS1-v1_5 with SHA-512 signing with wrong algorithm name]
expected: FAIL
[RSASSA-PKCS1-v1_5 with SHA-1 verification with wrong algorithm name]
expected: FAIL
[RSASSA-PKCS1-v1_5 with SHA-256 verification with wrong algorithm name]
expected: FAIL
[RSASSA-PKCS1-v1_5 with SHA-384 verification with wrong algorithm name]
expected: FAIL
[RSASSA-PKCS1-v1_5 with SHA-512 verification with wrong algorithm name]
expected: FAIL
[rsa_pkcs.https.any.html]
[importVectorKeys step: RSASSA-PKCS1-v1_5 with SHA-1 verification]
@@ -264,3 +288,27 @@
[importVectorKeys step: RSASSA-PKCS1-v1_5 with SHA-512 verification failure with altered plaintext]
expected: FAIL
[RSASSA-PKCS1-v1_5 with SHA-1 signing with wrong algorithm name]
expected: FAIL
[RSASSA-PKCS1-v1_5 with SHA-256 signing with wrong algorithm name]
expected: FAIL
[RSASSA-PKCS1-v1_5 with SHA-384 signing with wrong algorithm name]
expected: FAIL
[RSASSA-PKCS1-v1_5 with SHA-512 signing with wrong algorithm name]
expected: FAIL
[RSASSA-PKCS1-v1_5 with SHA-1 verification with wrong algorithm name]
expected: FAIL
[RSASSA-PKCS1-v1_5 with SHA-256 verification with wrong algorithm name]
expected: FAIL
[RSASSA-PKCS1-v1_5 with SHA-384 verification with wrong algorithm name]
expected: FAIL
[RSASSA-PKCS1-v1_5 with SHA-512 verification with wrong algorithm name]
expected: FAIL

View File

@@ -287,6 +287,246 @@
[importVectorKeys step: RSA-PSS with SHA-512, salted verification failure with altered plaintext]
expected: FAIL
[RSA-PSS with SHA-1 and no salt verification]
expected: FAIL
[RSA-PSS with SHA-256 and no salt verification]
expected: FAIL
[RSA-PSS with SHA-384 and no salt verification]
expected: FAIL
[RSA-PSS with SHA-512 and no salt verification]
expected: FAIL
[RSA-PSS with SHA-1, salted verification]
expected: FAIL
[RSA-PSS with SHA-256, salted verification]
expected: FAIL
[RSA-PSS with SHA-384, salted verification]
expected: FAIL
[RSA-PSS with SHA-512, salted verification]
expected: FAIL
[RSA-PSS with SHA-1 and no salt verification with altered signature after call]
expected: FAIL
[RSA-PSS with SHA-256 and no salt verification with altered signature after call]
expected: FAIL
[RSA-PSS with SHA-384 and no salt verification with altered signature after call]
expected: FAIL
[RSA-PSS with SHA-512 and no salt verification with altered signature after call]
expected: FAIL
[RSA-PSS with SHA-1, salted verification with altered signature after call]
expected: FAIL
[RSA-PSS with SHA-256, salted verification with altered signature after call]
expected: FAIL
[RSA-PSS with SHA-384, salted verification with altered signature after call]
expected: FAIL
[RSA-PSS with SHA-512, salted verification with altered signature after call]
expected: FAIL
[RSA-PSS with SHA-1 and no salt with altered plaintext after call]
expected: FAIL
[RSA-PSS with SHA-256 and no salt with altered plaintext after call]
expected: FAIL
[RSA-PSS with SHA-384 and no salt with altered plaintext after call]
expected: FAIL
[RSA-PSS with SHA-512 and no salt with altered plaintext after call]
expected: FAIL
[RSA-PSS with SHA-1, salted with altered plaintext after call]
expected: FAIL
[RSA-PSS with SHA-256, salted with altered plaintext after call]
expected: FAIL
[RSA-PSS with SHA-384, salted with altered plaintext after call]
expected: FAIL
[RSA-PSS with SHA-512, salted with altered plaintext after call]
expected: FAIL
[RSA-PSS with SHA-1 and no salt using privateKey to verify]
expected: FAIL
[RSA-PSS with SHA-256 and no salt using privateKey to verify]
expected: FAIL
[RSA-PSS with SHA-384 and no salt using privateKey to verify]
expected: FAIL
[RSA-PSS with SHA-512 and no salt using privateKey to verify]
expected: FAIL
[RSA-PSS with SHA-1, salted using privateKey to verify]
expected: FAIL
[RSA-PSS with SHA-256, salted using privateKey to verify]
expected: FAIL
[RSA-PSS with SHA-384, salted using privateKey to verify]
expected: FAIL
[RSA-PSS with SHA-512, salted using privateKey to verify]
expected: FAIL
[RSA-PSS with SHA-1 and no salt using publicKey to sign]
expected: FAIL
[RSA-PSS with SHA-256 and no salt using publicKey to sign]
expected: FAIL
[RSA-PSS with SHA-384 and no salt using publicKey to sign]
expected: FAIL
[RSA-PSS with SHA-512 and no salt using publicKey to sign]
expected: FAIL
[RSA-PSS with SHA-1, salted using publicKey to sign]
expected: FAIL
[RSA-PSS with SHA-256, salted using publicKey to sign]
expected: FAIL
[RSA-PSS with SHA-384, salted using publicKey to sign]
expected: FAIL
[RSA-PSS with SHA-512, salted using publicKey to sign]
expected: FAIL
[RSA-PSS with SHA-1 and no salt no verify usage]
expected: FAIL
[RSA-PSS with SHA-256 and no salt no verify usage]
expected: FAIL
[RSA-PSS with SHA-384 and no salt no verify usage]
expected: FAIL
[RSA-PSS with SHA-512 and no salt no verify usage]
expected: FAIL
[RSA-PSS with SHA-1, salted no verify usage]
expected: FAIL
[RSA-PSS with SHA-256, salted no verify usage]
expected: FAIL
[RSA-PSS with SHA-384, salted no verify usage]
expected: FAIL
[RSA-PSS with SHA-512, salted no verify usage]
expected: FAIL
[RSA-PSS with SHA-1 and no salt round trip]
expected: FAIL
[RSA-PSS with SHA-256 and no salt round trip]
expected: FAIL
[RSA-PSS with SHA-384 and no salt round trip]
expected: FAIL
[RSA-PSS with SHA-512 and no salt round trip]
expected: FAIL
[RSA-PSS with SHA-1, salted round trip]
expected: FAIL
[RSA-PSS with SHA-256, salted round trip]
expected: FAIL
[RSA-PSS with SHA-384, salted round trip]
expected: FAIL
[RSA-PSS with SHA-512, salted round trip]
expected: FAIL
[RSA-PSS with SHA-1 and no salt verification failure with altered signature]
expected: FAIL
[RSA-PSS with SHA-256 and no salt verification failure with altered signature]
expected: FAIL
[RSA-PSS with SHA-384 and no salt verification failure with altered signature]
expected: FAIL
[RSA-PSS with SHA-512 and no salt verification failure with altered signature]
expected: FAIL
[RSA-PSS with SHA-1, salted verification failure with altered signature]
expected: FAIL
[RSA-PSS with SHA-256, salted verification failure with altered signature]
expected: FAIL
[RSA-PSS with SHA-384, salted verification failure with altered signature]
expected: FAIL
[RSA-PSS with SHA-512, salted verification failure with altered signature]
expected: FAIL
[RSA-PSS with SHA-1 and no salt verification failure with wrong saltLength]
expected: FAIL
[RSA-PSS with SHA-256 and no salt verification failure with wrong saltLength]
expected: FAIL
[RSA-PSS with SHA-384 and no salt verification failure with wrong saltLength]
expected: FAIL
[RSA-PSS with SHA-512 and no salt verification failure with wrong saltLength]
expected: FAIL
[RSA-PSS with SHA-1, salted verification failure with wrong saltLength]
expected: FAIL
[RSA-PSS with SHA-256, salted verification failure with wrong saltLength]
expected: FAIL
[RSA-PSS with SHA-384, salted verification failure with wrong saltLength]
expected: FAIL
[RSA-PSS with SHA-512, salted verification failure with wrong saltLength]
expected: FAIL
[RSA-PSS with SHA-1 and no salt verification failure with altered plaintext]
expected: FAIL
[RSA-PSS with SHA-256 and no salt verification failure with altered plaintext]
expected: FAIL
[RSA-PSS with SHA-384 and no salt verification failure with altered plaintext]
expected: FAIL
[RSA-PSS with SHA-512 and no salt verification failure with altered plaintext]
expected: FAIL
[RSA-PSS with SHA-1, salted verification failure with altered plaintext]
expected: FAIL
[RSA-PSS with SHA-256, salted verification failure with altered plaintext]
expected: FAIL
[RSA-PSS with SHA-384, salted verification failure with altered plaintext]
expected: FAIL
[RSA-PSS with SHA-512, salted verification failure with altered plaintext]
expected: FAIL
[rsa_pss.https.any.html]
[importVectorKeys step: RSA-PSS with SHA-1 and no salt verification]
@@ -576,3 +816,243 @@
[importVectorKeys step: RSA-PSS with SHA-512, salted verification failure with altered plaintext]
expected: FAIL
[RSA-PSS with SHA-1 and no salt verification]
expected: FAIL
[RSA-PSS with SHA-256 and no salt verification]
expected: FAIL
[RSA-PSS with SHA-384 and no salt verification]
expected: FAIL
[RSA-PSS with SHA-512 and no salt verification]
expected: FAIL
[RSA-PSS with SHA-1, salted verification]
expected: FAIL
[RSA-PSS with SHA-256, salted verification]
expected: FAIL
[RSA-PSS with SHA-384, salted verification]
expected: FAIL
[RSA-PSS with SHA-512, salted verification]
expected: FAIL
[RSA-PSS with SHA-1 and no salt verification with altered signature after call]
expected: FAIL
[RSA-PSS with SHA-256 and no salt verification with altered signature after call]
expected: FAIL
[RSA-PSS with SHA-384 and no salt verification with altered signature after call]
expected: FAIL
[RSA-PSS with SHA-512 and no salt verification with altered signature after call]
expected: FAIL
[RSA-PSS with SHA-1, salted verification with altered signature after call]
expected: FAIL
[RSA-PSS with SHA-256, salted verification with altered signature after call]
expected: FAIL
[RSA-PSS with SHA-384, salted verification with altered signature after call]
expected: FAIL
[RSA-PSS with SHA-512, salted verification with altered signature after call]
expected: FAIL
[RSA-PSS with SHA-1 and no salt with altered plaintext after call]
expected: FAIL
[RSA-PSS with SHA-256 and no salt with altered plaintext after call]
expected: FAIL
[RSA-PSS with SHA-384 and no salt with altered plaintext after call]
expected: FAIL
[RSA-PSS with SHA-512 and no salt with altered plaintext after call]
expected: FAIL
[RSA-PSS with SHA-1, salted with altered plaintext after call]
expected: FAIL
[RSA-PSS with SHA-256, salted with altered plaintext after call]
expected: FAIL
[RSA-PSS with SHA-384, salted with altered plaintext after call]
expected: FAIL
[RSA-PSS with SHA-512, salted with altered plaintext after call]
expected: FAIL
[RSA-PSS with SHA-1 and no salt using privateKey to verify]
expected: FAIL
[RSA-PSS with SHA-256 and no salt using privateKey to verify]
expected: FAIL
[RSA-PSS with SHA-384 and no salt using privateKey to verify]
expected: FAIL
[RSA-PSS with SHA-512 and no salt using privateKey to verify]
expected: FAIL
[RSA-PSS with SHA-1, salted using privateKey to verify]
expected: FAIL
[RSA-PSS with SHA-256, salted using privateKey to verify]
expected: FAIL
[RSA-PSS with SHA-384, salted using privateKey to verify]
expected: FAIL
[RSA-PSS with SHA-512, salted using privateKey to verify]
expected: FAIL
[RSA-PSS with SHA-1 and no salt using publicKey to sign]
expected: FAIL
[RSA-PSS with SHA-256 and no salt using publicKey to sign]
expected: FAIL
[RSA-PSS with SHA-384 and no salt using publicKey to sign]
expected: FAIL
[RSA-PSS with SHA-512 and no salt using publicKey to sign]
expected: FAIL
[RSA-PSS with SHA-1, salted using publicKey to sign]
expected: FAIL
[RSA-PSS with SHA-256, salted using publicKey to sign]
expected: FAIL
[RSA-PSS with SHA-384, salted using publicKey to sign]
expected: FAIL
[RSA-PSS with SHA-512, salted using publicKey to sign]
expected: FAIL
[RSA-PSS with SHA-1 and no salt no verify usage]
expected: FAIL
[RSA-PSS with SHA-256 and no salt no verify usage]
expected: FAIL
[RSA-PSS with SHA-384 and no salt no verify usage]
expected: FAIL
[RSA-PSS with SHA-512 and no salt no verify usage]
expected: FAIL
[RSA-PSS with SHA-1, salted no verify usage]
expected: FAIL
[RSA-PSS with SHA-256, salted no verify usage]
expected: FAIL
[RSA-PSS with SHA-384, salted no verify usage]
expected: FAIL
[RSA-PSS with SHA-512, salted no verify usage]
expected: FAIL
[RSA-PSS with SHA-1 and no salt round trip]
expected: FAIL
[RSA-PSS with SHA-256 and no salt round trip]
expected: FAIL
[RSA-PSS with SHA-384 and no salt round trip]
expected: FAIL
[RSA-PSS with SHA-512 and no salt round trip]
expected: FAIL
[RSA-PSS with SHA-1, salted round trip]
expected: FAIL
[RSA-PSS with SHA-256, salted round trip]
expected: FAIL
[RSA-PSS with SHA-384, salted round trip]
expected: FAIL
[RSA-PSS with SHA-512, salted round trip]
expected: FAIL
[RSA-PSS with SHA-1 and no salt verification failure with altered signature]
expected: FAIL
[RSA-PSS with SHA-256 and no salt verification failure with altered signature]
expected: FAIL
[RSA-PSS with SHA-384 and no salt verification failure with altered signature]
expected: FAIL
[RSA-PSS with SHA-512 and no salt verification failure with altered signature]
expected: FAIL
[RSA-PSS with SHA-1, salted verification failure with altered signature]
expected: FAIL
[RSA-PSS with SHA-256, salted verification failure with altered signature]
expected: FAIL
[RSA-PSS with SHA-384, salted verification failure with altered signature]
expected: FAIL
[RSA-PSS with SHA-512, salted verification failure with altered signature]
expected: FAIL
[RSA-PSS with SHA-1 and no salt verification failure with wrong saltLength]
expected: FAIL
[RSA-PSS with SHA-256 and no salt verification failure with wrong saltLength]
expected: FAIL
[RSA-PSS with SHA-384 and no salt verification failure with wrong saltLength]
expected: FAIL
[RSA-PSS with SHA-512 and no salt verification failure with wrong saltLength]
expected: FAIL
[RSA-PSS with SHA-1, salted verification failure with wrong saltLength]
expected: FAIL
[RSA-PSS with SHA-256, salted verification failure with wrong saltLength]
expected: FAIL
[RSA-PSS with SHA-384, salted verification failure with wrong saltLength]
expected: FAIL
[RSA-PSS with SHA-512, salted verification failure with wrong saltLength]
expected: FAIL
[RSA-PSS with SHA-1 and no salt verification failure with altered plaintext]
expected: FAIL
[RSA-PSS with SHA-256 and no salt verification failure with altered plaintext]
expected: FAIL
[RSA-PSS with SHA-384 and no salt verification failure with altered plaintext]
expected: FAIL
[RSA-PSS with SHA-512 and no salt verification failure with altered plaintext]
expected: FAIL
[RSA-PSS with SHA-1, salted verification failure with altered plaintext]
expected: FAIL
[RSA-PSS with SHA-256, salted verification failure with altered plaintext]
expected: FAIL
[RSA-PSS with SHA-384, salted verification failure with altered plaintext]
expected: FAIL
[RSA-PSS with SHA-512, salted verification failure with altered plaintext]
expected: FAIL