script/dom/subtlecrypto/
rsassa_pkcs1_v1_5_operation.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use pkcs8::der::asn1::BitString;
6use pkcs8::der::{AnyRef, Decode};
7use pkcs8::{PrivateKeyInfo, SubjectPublicKeyInfo};
8use rsa::pkcs1::{self, DecodeRsaPrivateKey};
9use rsa::pkcs1v15::{Signature, SigningKey, VerifyingKey};
10use rsa::signature::{SignatureEncoding, Signer, Verifier};
11use rsa::traits::PublicKeyParts;
12use rsa::{BigUint, RsaPrivateKey, RsaPublicKey};
13use sha1::Sha1;
14use sha2::{Sha256, Sha384, Sha512};
15
16use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
17    CryptoKeyMethods, CryptoKeyPair, KeyType, KeyUsage,
18};
19use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{
20    AlgorithmIdentifier, JsonWebKey, KeyFormat,
21};
22use crate::dom::bindings::error::Error;
23use crate::dom::bindings::root::DomRoot;
24use crate::dom::bindings::str::DOMString;
25use crate::dom::cryptokey::{CryptoKey, Handle};
26use crate::dom::globalscope::GlobalScope;
27use crate::dom::subtlecrypto::rsa_common::{self, RsaAlgorithm};
28use crate::dom::subtlecrypto::{
29    ALG_RSASSA_PKCS1_V1_5, ALG_SHA1, ALG_SHA256, ALG_SHA384, ALG_SHA512, ExportedKey,
30    JsonWebKeyExt, JwkStringField, KeyAlgorithmAndDerivatives, Operation,
31    SubtleRsaHashedImportParams, SubtleRsaHashedKeyAlgorithm, SubtleRsaHashedKeyGenParams,
32    normalize_algorithm,
33};
34use crate::script_runtime::CanGc;
35
36/// <https://w3c.github.io/webcrypto/#rsassa-pkcs1-operations-sign>
37pub(crate) fn sign(key: &CryptoKey, message: &[u8]) -> Result<Vec<u8>, Error> {
38    // Step 1. If the [[type]] internal slot of key is not "private", then throw an
39    // InvalidAccessError.
40    if key.Type() != KeyType::Private {
41        return Err(Error::InvalidAccess(Some(
42            "[[type]] internal slot of key is not \"private\"".to_string(),
43        )));
44    }
45
46    // Step 2. Perform the signature generation operation defined in Section 8.2 of [RFC3447] with
47    // the key represented by the [[handle]] internal slot of key as the signer's private key and
48    // message as M and using the hash function specified in the hash attribute of the
49    // [[algorithm]] internal slot of key as the Hash option for the EMSA-PKCS1-v1_5 encoding
50    // method.
51    // Step 3. If performing the operation results in an error, then throw an OperationError.
52    // Step 4. Let signature be the value S that results from performing the operation.
53    let Handle::RsaPrivateKey(private_key) = key.handle() else {
54        return Err(Error::Operation(Some(
55            "[[handle]] internal slot of key is not an RSA private key".to_string(),
56        )));
57    };
58    let KeyAlgorithmAndDerivatives::RsaHashedKeyAlgorithm(algorithm) = key.algorithm() else {
59        return Err(Error::Operation(Some(
60            "[[algorithm]] internal slot of key is not an RsaHashedKeyAlgorithm".to_string(),
61        )));
62    };
63    let signature = match algorithm.hash.name() {
64        ALG_SHA1 => {
65            let signing_key = SigningKey::<Sha1>::new(private_key.clone());
66            signing_key.try_sign(message)
67        },
68        ALG_SHA256 => {
69            let signing_key = SigningKey::<Sha256>::new(private_key.clone());
70            signing_key.try_sign(message)
71        },
72        ALG_SHA384 => {
73            let signing_key = SigningKey::<Sha384>::new(private_key.clone());
74            signing_key.try_sign(message)
75        },
76        ALG_SHA512 => {
77            let signing_key = SigningKey::<Sha512>::new(private_key.clone());
78            signing_key.try_sign(message)
79        },
80        _ => {
81            return Err(Error::Operation(Some(format!(
82                "Unsupported \"{}\" hash for RSASSA-PKCS1-v1_5",
83                algorithm.hash.name()
84            ))));
85        },
86    }
87    .map_err(|_| Error::Operation(Some("RSASSA-PKCS1-v1_5 failed to sign message".to_string())))?;
88
89    // Step 5. Return signature.
90    Ok(signature.to_vec())
91}
92
93/// <https://w3c.github.io/webcrypto/#rsassa-pkcs1-operations-verify>
94pub(crate) fn verify(key: &CryptoKey, message: &[u8], signature: &[u8]) -> Result<bool, Error> {
95    // Step 1. If the [[type]] internal slot of key is not "public", then throw an
96    // InvalidAccessError.
97    if key.Type() != KeyType::Public {
98        return Err(Error::InvalidAccess(Some(
99            "[[type]] internal slot of key is not \"public\"".to_string(),
100        )));
101    }
102
103    // Step 2. Perform the signature verification operation defined in Section 8.2 of [RFC3447]
104    // with the key represented by the [[handle]] internal slot of key as the signer's RSA public
105    // key and message as M and signature as S and using the hash function specified in the hash
106    // attribute of the [[algorithm]] internal slot of key as the Hash option for the
107    // EMSA-PKCS1-v1_5 encoding method.
108    // Step 3. Let result be a boolean with value true if the result of the operation was "valid
109    // signature" and the value false otherwise.
110    let Handle::RsaPublicKey(public_key) = key.handle() else {
111        return Err(Error::Operation(Some(
112            "[[handle]] internal slot of key is not an RSA public key".to_string(),
113        )));
114    };
115    let KeyAlgorithmAndDerivatives::RsaHashedKeyAlgorithm(algorithm) = key.algorithm() else {
116        return Err(Error::Operation(Some(
117            "[[algorithm]] internal slot of key is not an RsaHashedKeyAlgorithm".to_string(),
118        )));
119    };
120    let signature = Signature::try_from(signature)
121        .map_err(|_| Error::Operation(Some("Failed to parse RSA signature".to_string())))?;
122    let result = match algorithm.hash.name() {
123        ALG_SHA1 => {
124            let verifying_key = VerifyingKey::<Sha1>::new(public_key.clone());
125            verifying_key.verify(message, &signature)
126        },
127        ALG_SHA256 => {
128            let verifying_key = VerifyingKey::<Sha256>::new(public_key.clone());
129            verifying_key.verify(message, &signature)
130        },
131        ALG_SHA384 => {
132            let verifying_key = VerifyingKey::<Sha384>::new(public_key.clone());
133            verifying_key.verify(message, &signature)
134        },
135        ALG_SHA512 => {
136            let verifying_key = VerifyingKey::<Sha512>::new(public_key.clone());
137            verifying_key.verify(message, &signature)
138        },
139        _ => {
140            return Err(Error::Operation(Some(format!(
141                "Unsupported \"{}\" hash for RSASSA-PKCS1-v1_5",
142                algorithm.hash.name()
143            ))));
144        },
145    }
146    .is_ok();
147
148    // Step 4. Return result.
149    Ok(result)
150}
151
152/// <https://w3c.github.io/webcrypto/#rsassa-pkcs1-operations-generate-key>
153pub(crate) fn generate_key(
154    global: &GlobalScope,
155    normalized_algorithm: &SubtleRsaHashedKeyGenParams,
156    extractable: bool,
157    usages: Vec<KeyUsage>,
158    can_gc: CanGc,
159) -> Result<CryptoKeyPair, Error> {
160    rsa_common::generate_key(
161        RsaAlgorithm::RsaSsaPkcs1v15,
162        global,
163        normalized_algorithm,
164        extractable,
165        usages,
166        can_gc,
167    )
168}
169
170/// <https://w3c.github.io/webcrypto/#rsassa-pkcs1-operations-import-key>
171pub(crate) fn import_key(
172    global: &GlobalScope,
173    normalized_algorithm: &SubtleRsaHashedImportParams,
174    format: KeyFormat,
175    key_data: &[u8],
176    extractable: bool,
177    usages: Vec<KeyUsage>,
178    can_gc: CanGc,
179) -> Result<DomRoot<CryptoKey>, Error> {
180    // Step 1. Let keyData be the key data to be imported.
181
182    // Step 2.
183    let (key_handle, key_type) = match format {
184        // If format is "spki":
185        KeyFormat::Spki => {
186            // Step 2.1. If usages contains an entry which is not "verify", then throw a
187            // SyntaxError.
188            if usages.iter().any(|usage| *usage != KeyUsage::Verify) {
189                return Err(Error::Syntax(Some(
190                    "Usages contains an entry which is not \"verify\"".to_string(),
191                )));
192            }
193
194            // Step 2.2. Let spki be the result of running the parse a subjectPublicKeyInfo
195            // algorithm over keyData.
196            // Step 2.3. If an error occurred while parsing, then throw a DataError.
197            let spki =
198                SubjectPublicKeyInfo::<AnyRef, BitString>::from_der(key_data).map_err(|_| {
199                    Error::Data(Some(
200                        "Fail to parse SubjectPublicKeyInfo over keyData".to_string(),
201                    ))
202                })?;
203
204            // Step 2.4. If the algorithm object identifier field of the algorithm
205            // AlgorithmIdentifier field of spki is not equal to the rsaEncryption object
206            // identifier defined in [RFC3447], then throw a DataError.
207            if spki.algorithm.oid != pkcs1::ALGORITHM_OID {
208                return Err(Error::Data(Some(
209                    "Algorithm object identifier of spki is not an rsaEncryption".to_string(),
210                )));
211            }
212
213            // Step 2.5. Let publicKey be the result of performing the parse an ASN.1 structure
214            // algorithm, with data as the subjectPublicKeyInfo field of spki, structure as the
215            // RSAPublicKey structure specified in Section A.1.1 of [RFC3447], and exactData set to
216            // true.
217            // Step 2.6. If an error occurred while parsing, or it can be determined that publicKey
218            // is not a valid public key according to [RFC3447], then throw a DataError.
219            let pkcs1_bytes = spki.subject_public_key.as_bytes().ok_or(Error::Data(Some(
220                "Fail to parse byte sequence over SubjectPublicKey field of spki".to_string(),
221            )))?;
222            let rsa_public_key_structure =
223                pkcs1::RsaPublicKey::try_from(pkcs1_bytes).map_err(|_| {
224                    Error::Data(Some(
225                        "SubjectPublicKey field of spki is not an RSAPublicKey structure"
226                            .to_string(),
227                    ))
228                })?;
229            let n = BigUint::from_bytes_be(rsa_public_key_structure.modulus.as_bytes());
230            let e = BigUint::from_bytes_be(rsa_public_key_structure.public_exponent.as_bytes());
231            let public_key = RsaPublicKey::new(n, e).map_err(|_| {
232                Error::Data(Some(
233                    "Fail to construct RSA public key from modulus and public exponent".to_string(),
234                ))
235            })?;
236
237            // Step 2.7. Let key be a new CryptoKey that represents the RSA public key identified
238            // by publicKey.
239            // Step 2.8. Set the [[type]] internal slot of key to "public"
240            // NOTE: Done in Step 3-8.
241            let key_handle = Handle::RsaPublicKey(public_key);
242            let key_type = KeyType::Public;
243            (key_handle, key_type)
244        },
245        // If format is "pkcs8":
246        KeyFormat::Pkcs8 => {
247            // Step 2.1. If usages contains an entry which is not "sign" then throw a SyntaxError.
248            if usages.iter().any(|usage| *usage != KeyUsage::Sign) {
249                return Err(Error::Syntax(Some(
250                    "Usages contains an entry which is not \"sign\"".to_string(),
251                )));
252            }
253
254            // Step 2.2. Let privateKeyInfo be the result of running the parse a privateKeyInfo
255            // algorithm over keyData.
256            // Step 2.3. If an error occurred while parsing, then throw a DataError.
257            let private_key_info = PrivateKeyInfo::from_der(key_data).map_err(|_| {
258                Error::Data(Some(
259                    "Fail to parse PrivateKeyInfo over keyData".to_string(),
260                ))
261            })?;
262
263            // Step 2.4. If the algorithm object identifier field of the privateKeyAlgorithm
264            // PrivateKeyAlgorithm field of privateKeyInfo is not equal to the rsaEncryption object
265            // identifier defined in [RFC3447], then throw a DataError.
266            if private_key_info.algorithm.oid != pkcs1::ALGORITHM_OID {
267                return Err(Error::Data(Some(
268                    "Algorithm object identifier of PrivateKeyInfo is not an rsaEncryption"
269                        .to_string(),
270                )));
271            }
272
273            // Step 2.5. Let rsaPrivateKey be the result of performing the parse an ASN.1 structure
274            // algorithm, with data as the privateKey field of privateKeyInfo, structure as the
275            // RSAPrivateKey structure specified in Section A.1.2 of [RFC3447], and exactData set
276            // to true.
277            // Step 2.6. If an error occurred while parsing, or if rsaPrivateKey is not a valid RSA
278            // private key according to [RFC3447], then throw a DataError.
279            let rsa_private_key = RsaPrivateKey::from_pkcs1_der(private_key_info.private_key)
280                .map_err(|_| {
281                    Error::Data(Some(
282                        "PrivateKey field of PrivateKeyInfo is not an RSAPrivateKey structure"
283                            .to_string(),
284                    ))
285                })?;
286
287            // Step 2.7. Let key be a new CryptoKey that represents the RSA private key identified
288            // by rsaPrivateKey.
289            // Step 2.8. Set the [[type]] internal slot of key to "private"
290            // NOTE: Done in Step 3-8.
291            let key_handle = Handle::RsaPrivateKey(rsa_private_key);
292            let key_type = KeyType::Private;
293            (key_handle, key_type)
294        },
295        // If format is "jwk":
296        KeyFormat::Jwk => {
297            // Step 2.1.
298            // If keyData is a JsonWebKey dictionary:
299            //     Let jwk equal keyData.
300            // Otherwise:
301            //     Throw a DataError.
302            let cx = GlobalScope::get_cx();
303            let jwk = JsonWebKey::parse(cx, key_data)?;
304
305            // Step 2.2. If the d field of jwk is present and usages contains an entry which is not
306            // "sign", or, if the d field of jwk is not present and usages contains an entry which
307            // is not "verify" then throw a SyntaxError.
308            if jwk.d.is_some() && usages.iter().any(|usage| *usage != KeyUsage::Sign) {
309                return Err(Error::Syntax(Some(
310                    "The d field of jwk is present and usages contains an entry which is not \"sign\""
311                        .to_string()
312                )));
313            }
314            if jwk.d.is_none() && usages.iter().any(|usage| *usage != KeyUsage::Verify) {
315                return Err(Error::Syntax(Some(
316                    "The d field of jwk is not present and usages contains an entry which is not \"verify\""
317                        .to_string()
318                )));
319            }
320
321            // Step 2.3. If the kty field of jwk is not a case-sensitive string match to "RSA",
322            // then throw a DataError.
323            if jwk.kty.as_ref().is_none_or(|kty| kty != "RSA") {
324                return Err(Error::Data(Some(
325                    "The kty field of jwk is not a case-sensitive string match to \"RSA\""
326                        .to_string(),
327                )));
328            }
329
330            // Step 2.4. If usages is non-empty and the use field of jwk is present and is not a
331            // case-sensitive string match to "sig", then throw a DataError.
332            if !usages.is_empty() && jwk.use_.as_ref().is_some_and(|use_| use_ != "sig") {
333                return Err(Error::Data(Some(
334                    "Usages is non-empty and the use field of jwk is present and \
335                    is not a case-sensitive string match to \"sig\""
336                        .to_string(),
337                )));
338            }
339
340            // Step 2.5. If the key_ops field of jwk is present, and is invalid according to the
341            // requirements of JSON Web Key [JWK] or does not contain all of the specified usages
342            // values, then throw a DataError.
343            jwk.check_key_ops(&usages)?;
344
345            // Step 2.6. If the ext field of jwk is present and has the value false and extractable
346            // is true, then throw a DataError.
347            if jwk.ext.is_some_and(|ext| !ext) && extractable {
348                return Err(Error::Data(Some(
349                    "The ext field of jwk is present and \
350                    has the value false and extractable is true"
351                        .to_string(),
352                )));
353            }
354
355            // Step 2.7. Let hash be a be a string whose initial value is undefined.
356            // Step 2.8.
357            // If the alg field of jwk is not present:
358            //     Let hash be undefined.
359            // If the alg field is equal to the string "RS1":
360            //     Let hash be the string "SHA-1".
361            // If the alg field is equal to the string "RS256":
362            //     Let hash be the string "SHA-256".
363            // If the alg field is equal to the string "RS384":
364            //     Let hash be the string "SHA-384".
365            // If the alg field is equal to the string "RS512":
366            //     Let hash be the string "SHA-512".
367            // Otherwise:
368            //     Perform any key import steps defined by other applicable specifications, passing
369            //     format, jwk and obtaining hash.
370            //     If an error occurred or there are no applicable specifications, throw a DataError.
371            let hash = match &jwk.alg {
372                None => None,
373                Some(alg) => match &*alg.str() {
374                    "RS1" => Some(ALG_SHA1),
375                    "RS256" => Some(ALG_SHA256),
376                    "RS384" => Some(ALG_SHA384),
377                    "RS512" => Some(ALG_SHA512),
378                    _ => None,
379                },
380            };
381
382            // Step 2.9. If hash is not undefined:
383            if let Some(hash) = hash {
384                // Step 2.9.1. Let normalizedHash be the result of normalize an algorithm with alg
385                // set to hash and op set to digest.
386                let normalized_hash = normalize_algorithm(
387                    cx,
388                    Operation::Digest,
389                    &AlgorithmIdentifier::String(DOMString::from(hash)),
390                    can_gc,
391                )?;
392
393                // Step 2.9.2. If normalizedHash is not equal to the hash member of
394                // normalizedAlgorithm, throw a DataError.
395                if normalized_hash.name() != normalized_algorithm.hash.name() {
396                    return Err(Error::Data(Some(
397                        "The normalizedHash is not equal to the hash member of normalizedAlgorithm"
398                            .to_string(),
399                    )));
400                }
401            }
402
403            // Step 2.10.
404            // If the d field of jwk is present:
405            if jwk.d.is_some() {
406                // Step 2.10.1. If jwk does not meet the requirements of Section 6.3.2 of JSON Web
407                // Algorithms [JWA], then throw a DataError.
408                let n = jwk.decode_required_string_field(JwkStringField::N)?;
409                let e = jwk.decode_required_string_field(JwkStringField::E)?;
410                let d = jwk.decode_required_string_field(JwkStringField::D)?;
411                let p = jwk.decode_optional_string_field(JwkStringField::P)?;
412                let q = jwk.decode_optional_string_field(JwkStringField::Q)?;
413                let dp = jwk.decode_optional_string_field(JwkStringField::DP)?;
414                let dq = jwk.decode_optional_string_field(JwkStringField::DQ)?;
415                let qi = jwk.decode_optional_string_field(JwkStringField::QI)?;
416                let mut primes = match (p, q, dp, dq, qi) {
417                    (Some(p), Some(q), Some(_dp), Some(_dq), Some(_qi)) => vec![p, q],
418                    (None, None, None, None, None) => Vec::new(),
419                    _ => return Err(Error::Data(Some(
420                        "The p, q, dp, dq, qi fields of jwk must be either all-present or all-absent"
421                            .to_string()
422                    ))),
423                };
424                jwk.decode_primes_from_oth_field(&mut primes)?;
425
426                // Step 2.10.2. Let privateKey represents the RSA private key identified by
427                // interpreting jwk according to Section 6.3.2 of JSON Web Algorithms [JWA].
428                // Step 2.10.3. If privateKey is not a valid RSA private key according to
429                // [RFC3447], then throw a DataError.
430                let private_key = RsaPrivateKey::from_components(
431                    BigUint::from_bytes_be(&n),
432                    BigUint::from_bytes_be(&e),
433                    BigUint::from_bytes_be(&d),
434                    primes
435                        .into_iter()
436                        .map(|prime| BigUint::from_bytes_be(&prime))
437                        .collect(),
438                )
439                .map_err(|_| {
440                    Error::Data(Some(
441                        "Failed to construct RSA private key from values in jwk".to_string(),
442                    ))
443                })?;
444
445                // Step 2.10.4. Let key be a new CryptoKey object that represents privateKey.
446                // Step 2.10.5. Set the [[type]] internal slot of key to "private"
447                // NOTE: Done in Step 3-8.
448                let key_handle = Handle::RsaPrivateKey(private_key);
449                let key_type = KeyType::Private;
450                (key_handle, key_type)
451            }
452            // Otherwise:
453            else {
454                // Step 2.10.1. If jwk does not meet the requirements of Section 6.3.1 of JSON Web
455                // Algorithms [JWA], then throw a DataError.
456                let n = jwk.decode_required_string_field(JwkStringField::N)?;
457                let e = jwk.decode_required_string_field(JwkStringField::E)?;
458
459                // Step 2.10.2. Let publicKey represent the RSA public key identified by
460                // interpreting jwk according to Section 6.3.1 of JSON Web Algorithms [JWA].
461                // Step 2.10.3. If publicKey can be determined to not be a valid RSA public key
462                // according to [RFC3447], then throw a DataError.
463                let public_key =
464                    RsaPublicKey::new(BigUint::from_bytes_be(&n), BigUint::from_bytes_be(&e))
465                        .map_err(|_| {
466                            Error::Data(Some(
467                                "Failed to construct RSA public key from values in jwk".to_string(),
468                            ))
469                        })?;
470
471                // Step 2.10.4. Let key be a new CryptoKey representing publicKey.
472                // Step 2.10.5. Set the [[type]] internal slot of key to "public"
473                // NOTE: Done in Step 3-8.
474                let key_handle = Handle::RsaPublicKey(public_key);
475                let key_type = KeyType::Public;
476                (key_handle, key_type)
477            }
478        },
479        // Otherwise:
480        _ => {
481            // throw a NotSupportedError.
482            return Err(Error::NotSupported(Some(
483                "Unsupported import key format for RSA key".to_string(),
484            )));
485        },
486    };
487
488    // Step 3. Let algorithm be a new RsaHashedKeyAlgorithm dictionary.
489    // Step 4. Set the name attribute of algorithm to "RSASSA-PKCS1-v1_5"
490    // Step 5. Set the modulusLength attribute of algorithm to the length, in bits, of the RSA
491    // public modulus.
492    // Step 6. Set the publicExponent attribute of algorithm to the BigInteger representation of
493    // the RSA public exponent.
494    // Step 7. Set the hash attribute of algorithm to the hash member of normalizedAlgorithm.
495    // Step 8. Set the [[algorithm]] internal slot of key to algorithm.
496    let (modulus_length, public_exponent) = match &key_handle {
497        Handle::RsaPrivateKey(private_key) => {
498            (private_key.size() as u32 * 8, private_key.e().to_bytes_be())
499        },
500        Handle::RsaPublicKey(public_key) => {
501            (public_key.size() as u32 * 8, public_key.e().to_bytes_be())
502        },
503        _ => unreachable!(),
504    };
505    let algorithm = SubtleRsaHashedKeyAlgorithm {
506        name: ALG_RSASSA_PKCS1_V1_5.to_string(),
507        modulus_length,
508        public_exponent,
509        hash: normalized_algorithm.hash.clone(),
510    };
511    let key = CryptoKey::new(
512        global,
513        key_type,
514        extractable,
515        KeyAlgorithmAndDerivatives::RsaHashedKeyAlgorithm(algorithm),
516        usages,
517        key_handle,
518        can_gc,
519    );
520
521    // Step 9. Return key.
522    Ok(key)
523}
524
525/// <https://w3c.github.io/webcrypto/#rsassa-pkcs1-operations-export-key>
526pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
527    rsa_common::export_key(RsaAlgorithm::RsaSsaPkcs1v15, format, key)
528}