Skip to main content

script_webcrypto/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 js::context::JSContext;
6use rsa::pkcs1v15::{Signature, SigningKey, VerifyingKey};
7use rsa::signature::{SignatureEncoding, Signer, Verifier};
8use script_bindings::codegen::GenericBindings::CryptoKeyBinding::{
9    CryptoKeyPair, KeyType, KeyUsage,
10};
11use script_bindings::codegen::GenericBindings::SubtleCryptoBinding::KeyFormat;
12use script_bindings::error::Error;
13use script_bindings::root::DomRoot;
14use sha1::Sha1;
15use sha2::{Sha256, Sha384, Sha512};
16
17use crate::cryptokey::{CryptoKey, Handle};
18use crate::subtlecrypto::rsa_common::{self, RsaAlgorithm};
19use crate::subtlecrypto::{
20    CryptoAlgorithm, ExportedKey, KeyAlgorithmAndDerivatives, NormalizedAlgorithm,
21    RsaHashedImportParams, RsaHashedKeyGenParams,
22};
23use crate::traits::Equivalence;
24
25/// <https://w3c.github.io/webcrypto/#rsassa-pkcs1-operations-sign>
26pub(crate) fn sign<D: Equivalence>(key: &CryptoKey<D>, message: &[u8]) -> Result<Vec<u8>, Error> {
27    // Step 1. If the [[type]] internal slot of key is not "private", then throw an
28    // InvalidAccessError.
29    key.ensure_type(KeyType::Private)?;
30
31    // Step 2. Perform the signature generation operation defined in Section 8.2 of [RFC3447] with
32    // the key represented by the [[handle]] internal slot of key as the signer's private key and
33    // message as M and using the hash function specified in the hash attribute of the
34    // [[algorithm]] internal slot of key as the Hash option for the EMSA-PKCS1-v1_5 encoding
35    // method.
36    // Step 3. If performing the operation results in an error, then throw an OperationError.
37    // Step 4. Let signature be the value S that results from performing the operation.
38    let Handle::RsaPrivateKey(private_key) = key.handle() else {
39        return Err(Error::Operation(Some(
40            "[[handle]] internal slot of key is not an RSA private key".to_string(),
41        )));
42    };
43    let KeyAlgorithmAndDerivatives::RsaHashedKeyAlgorithm(algorithm) = key.algorithm() else {
44        return Err(Error::Operation(Some(
45            "[[algorithm]] internal slot of key is not an RsaHashedKeyAlgorithm".to_string(),
46        )));
47    };
48    let signature = match algorithm.hash.name() {
49        CryptoAlgorithm::Sha1 => {
50            let signing_key = SigningKey::<Sha1>::new(private_key.clone());
51            signing_key.try_sign(message)
52        },
53        CryptoAlgorithm::Sha256 => {
54            let signing_key = SigningKey::<Sha256>::new(private_key.clone());
55            signing_key.try_sign(message)
56        },
57        CryptoAlgorithm::Sha384 => {
58            let signing_key = SigningKey::<Sha384>::new(private_key.clone());
59            signing_key.try_sign(message)
60        },
61        CryptoAlgorithm::Sha512 => {
62            let signing_key = SigningKey::<Sha512>::new(private_key.clone());
63            signing_key.try_sign(message)
64        },
65        _ => {
66            return Err(Error::Operation(Some(format!(
67                "Unsupported \"{}\" hash for RSASSA-PKCS1-v1_5",
68                algorithm.hash.name().as_str()
69            ))));
70        },
71    }
72    .map_err(|_| Error::Operation(Some("RSASSA-PKCS1-v1_5 failed to sign message".to_string())))?;
73
74    // Step 5. Return signature.
75    Ok(signature.to_vec())
76}
77
78/// <https://w3c.github.io/webcrypto/#rsassa-pkcs1-operations-verify>
79pub(crate) fn verify<D: Equivalence>(
80    key: &CryptoKey<D>,
81    message: &[u8],
82    signature: &[u8],
83) -> Result<bool, Error> {
84    // Step 1. If the [[type]] internal slot of key is not "public", then throw an
85    // InvalidAccessError.
86    key.ensure_type(KeyType::Public)?;
87
88    // Step 2. Perform the signature verification operation defined in Section 8.2 of [RFC3447]
89    // with the key represented by the [[handle]] internal slot of key as the signer's RSA public
90    // key and message as M and signature as S and using the hash function specified in the hash
91    // attribute of the [[algorithm]] internal slot of key as the Hash option for the
92    // EMSA-PKCS1-v1_5 encoding method.
93    // Step 3. Let result be a boolean with value true if the result of the operation was "valid
94    // signature" and the value false otherwise.
95    let Handle::RsaPublicKey(public_key) = key.handle() else {
96        return Err(Error::Operation(Some(
97            "[[handle]] internal slot of key is not an RSA public key".to_string(),
98        )));
99    };
100    let KeyAlgorithmAndDerivatives::RsaHashedKeyAlgorithm(algorithm) = key.algorithm() else {
101        return Err(Error::Operation(Some(
102            "[[algorithm]] internal slot of key is not an RsaHashedKeyAlgorithm".to_string(),
103        )));
104    };
105    let signature = Signature::try_from(signature)
106        .map_err(|_| Error::Operation(Some("Failed to parse RSA signature".to_string())))?;
107    let result = match algorithm.hash.name() {
108        CryptoAlgorithm::Sha1 => {
109            let verifying_key = VerifyingKey::<Sha1>::new(public_key.clone());
110            verifying_key.verify(message, &signature)
111        },
112        CryptoAlgorithm::Sha256 => {
113            let verifying_key = VerifyingKey::<Sha256>::new(public_key.clone());
114            verifying_key.verify(message, &signature)
115        },
116        CryptoAlgorithm::Sha384 => {
117            let verifying_key = VerifyingKey::<Sha384>::new(public_key.clone());
118            verifying_key.verify(message, &signature)
119        },
120        CryptoAlgorithm::Sha512 => {
121            let verifying_key = VerifyingKey::<Sha512>::new(public_key.clone());
122            verifying_key.verify(message, &signature)
123        },
124        _ => {
125            return Err(Error::Operation(Some(format!(
126                "Unsupported \"{}\" hash for RSASSA-PKCS1-v1_5",
127                algorithm.hash.name().as_str()
128            ))));
129        },
130    }
131    .is_ok();
132
133    // Step 4. Return result.
134    Ok(result)
135}
136
137/// <https://w3c.github.io/webcrypto/#rsassa-pkcs1-operations-generate-key>
138pub(crate) fn generate_key<D: Equivalence>(
139    cx: &mut JSContext,
140    global: &D::GlobalScope,
141    normalized_algorithm: &RsaHashedKeyGenParams,
142    extractable: bool,
143    usages: Vec<KeyUsage>,
144) -> Result<CryptoKeyPair<D>, Error> {
145    rsa_common::generate_key(
146        cx,
147        global,
148        RsaAlgorithm::RsassaPkcs1v1_5,
149        normalized_algorithm,
150        extractable,
151        usages,
152    )
153}
154
155/// <https://w3c.github.io/webcrypto/#rsassa-pkcs1-operations-import-key>
156pub(crate) fn import_key<D: Equivalence>(
157    cx: &mut JSContext,
158    global: &D::GlobalScope,
159    normalized_algorithm: &RsaHashedImportParams,
160    format: KeyFormat,
161    key_data: &[u8],
162    extractable: bool,
163    usages: Vec<KeyUsage>,
164) -> Result<DomRoot<CryptoKey<D>>, Error> {
165    rsa_common::import_key(
166        cx,
167        global,
168        RsaAlgorithm::RsassaPkcs1v1_5,
169        normalized_algorithm,
170        format,
171        key_data,
172        extractable,
173        usages,
174    )
175}
176
177/// <https://w3c.github.io/webcrypto/#rsassa-pkcs1-operations-export-key>
178pub(crate) fn export_key<D: Equivalence>(
179    format: KeyFormat,
180    key: &CryptoKey<D>,
181) -> Result<ExportedKey, Error> {
182    rsa_common::export_key(RsaAlgorithm::RsassaPkcs1v1_5, format, key)
183}
184
185/// <https://wicg.github.io/webcrypto-modern-algos/#SubtleCrypto-method-getPublicKey>
186/// Step 9 - 15, for RSASSA-PKCS1-v1_5
187pub(crate) fn get_public_key<D: Equivalence>(
188    cx: &mut JSContext,
189    global: &D::GlobalScope,
190    key: &CryptoKey<D>,
191    algorithm: &KeyAlgorithmAndDerivatives,
192    usages: Vec<KeyUsage>,
193) -> Result<DomRoot<CryptoKey<D>>, Error> {
194    rsa_common::get_public_key(
195        cx,
196        global,
197        RsaAlgorithm::RsassaPkcs1v1_5,
198        key,
199        algorithm,
200        usages,
201    )
202}