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}