script/dom/webcrypto/subtlecrypto/
ecdsa_operation.rs1use digest::Digest;
6use ecdsa::signature::hazmat::{PrehashVerifier, RandomizedPrehashSigner};
7use ecdsa::{Signature, SigningKey, VerifyingKey};
8use elliptic_curve::rand_core::OsRng;
9use js::context::JSContext;
10use p256::NistP256;
11use p384::NistP384;
12use p521::NistP521;
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::KeyFormat;
20use crate::dom::bindings::error::Error;
21use crate::dom::bindings::root::DomRoot;
22use crate::dom::cryptokey::{CryptoKey, Handle};
23use crate::dom::globalscope::GlobalScope;
24use crate::dom::subtlecrypto::ec_common::EcAlgorithm;
25use crate::dom::subtlecrypto::{
26 CryptoAlgorithm, ExportedKey, KeyAlgorithmAndDerivatives, NAMED_CURVE_P256, NAMED_CURVE_P384,
27 NAMED_CURVE_P521, NormalizedAlgorithm, SubtleEcKeyGenParams, SubtleEcKeyImportParams,
28 SubtleEcdsaParams, ec_common,
29};
30
31const P256_PREHASH_LENGTH: usize = 32;
32const P384_PREHASH_LENGTH: usize = 48;
33const P521_PREHASH_LENGTH: usize = 66;
34
35pub(crate) fn sign(
37 normalized_algorithm: &SubtleEcdsaParams,
38 key: &CryptoKey,
39 message: &[u8],
40) -> Result<Vec<u8>, Error> {
41 if key.Type() != KeyType::Private {
44 return Err(Error::InvalidAccess(None));
45 }
46
47 let hash_algorithm = &normalized_algorithm.hash;
49
50 let m = match hash_algorithm.name() {
53 CryptoAlgorithm::Sha1 => Sha1::new_with_prefix(message).finalize().to_vec(),
54 CryptoAlgorithm::Sha256 => Sha256::new_with_prefix(message).finalize().to_vec(),
55 CryptoAlgorithm::Sha384 => Sha384::new_with_prefix(message).finalize().to_vec(),
56 CryptoAlgorithm::Sha512 => Sha512::new_with_prefix(message).finalize().to_vec(),
57 _ => return Err(Error::NotSupported(None)),
58 };
59
60 let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = key.algorithm() else {
81 return Err(Error::Operation(None));
82 };
83 let result = match algorithm.named_curve.as_str() {
84 NAMED_CURVE_P256 => {
85 let m = expand_prehash(m, P256_PREHASH_LENGTH);
88
89 let Handle::P256PrivateKey(d) = key.handle() else {
90 return Err(Error::Operation(None));
91 };
92 let signing_key = SigningKey::<NistP256>::from(d);
93 let signature: Signature<NistP256> = signing_key
94 .sign_prehash_with_rng(&mut OsRng, m.as_slice())
95 .map_err(|_| Error::Operation(None))?;
96 signature.to_vec()
97 },
98 NAMED_CURVE_P384 => {
99 let m = expand_prehash(m, P384_PREHASH_LENGTH);
102
103 let Handle::P384PrivateKey(d) = key.handle() else {
104 return Err(Error::Operation(None));
105 };
106 let signing_key = SigningKey::<NistP384>::from(d);
107 let signature: Signature<NistP384> = signing_key
108 .sign_prehash_with_rng(&mut OsRng, m.as_slice())
109 .map_err(|_| Error::Abort(None))?;
110 signature.to_vec()
111 },
112 NAMED_CURVE_P521 => {
113 let m = expand_prehash(m, P521_PREHASH_LENGTH);
116
117 let Handle::P521PrivateKey(d) = key.handle() else {
118 return Err(Error::Operation(None));
119 };
120 let signing_key = p521::ecdsa::SigningKey::from_slice(d.to_bytes().as_slice())
121 .map_err(|_| Error::Operation(None))?;
122 let signature: Signature<NistP521> = signing_key
123 .sign_prehash_with_rng(&mut OsRng, m.as_slice())
124 .map_err(|_| Error::Operation(None))?;
125 signature.to_vec()
126 },
127 _ => return Err(Error::NotSupported(None)),
128 };
129
130 Ok(result)
132}
133
134pub(crate) fn verify(
136 normalized_algorithm: &SubtleEcdsaParams,
137 key: &CryptoKey,
138 message: &[u8],
139 signature: &[u8],
140) -> Result<bool, Error> {
141 if key.Type() != KeyType::Public {
144 return Err(Error::InvalidAccess(None));
145 }
146
147 let hash_algorithm = &normalized_algorithm.hash;
149
150 let m = match hash_algorithm.name() {
153 CryptoAlgorithm::Sha1 => Sha1::new_with_prefix(message).finalize().to_vec(),
154 CryptoAlgorithm::Sha256 => Sha256::new_with_prefix(message).finalize().to_vec(),
155 CryptoAlgorithm::Sha384 => Sha384::new_with_prefix(message).finalize().to_vec(),
156 CryptoAlgorithm::Sha512 => Sha512::new_with_prefix(message).finalize().to_vec(),
157 _ => return Err(Error::NotSupported(None)),
158 };
159
160 let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = key.algorithm() else {
182 return Err(Error::Operation(None));
183 };
184 let result = match algorithm.named_curve.as_str() {
185 NAMED_CURVE_P256 => {
186 let m = expand_prehash(m, P256_PREHASH_LENGTH);
189
190 let Handle::P256PublicKey(q) = key.handle() else {
191 return Err(Error::Operation(None));
192 };
193 match Signature::<NistP256>::from_slice(signature) {
194 Ok(signature) => {
195 let verifying_key = VerifyingKey::<NistP256>::from(q);
196 verifying_key.verify_prehash(&m, &signature).is_ok()
197 },
198 Err(_) => false,
199 }
200 },
201 NAMED_CURVE_P384 => {
202 let m = expand_prehash(m, P384_PREHASH_LENGTH);
205
206 let Handle::P384PublicKey(q) = key.handle() else {
207 return Err(Error::Operation(None));
208 };
209 match Signature::<NistP384>::from_slice(signature) {
210 Ok(signature) => {
211 let verifying_key = VerifyingKey::<NistP384>::from(q);
212 verifying_key.verify_prehash(&m, &signature).is_ok()
213 },
214 Err(_) => false,
215 }
216 },
217 NAMED_CURVE_P521 => {
218 let m = expand_prehash(m, P521_PREHASH_LENGTH);
221
222 let Handle::P521PublicKey(q) = key.handle() else {
223 return Err(Error::Operation(None));
224 };
225 match (
226 Signature::<NistP521>::from_slice(signature),
227 p521::ecdsa::VerifyingKey::from_sec1_bytes(q.to_sec1_bytes().to_vec().as_slice()),
228 ) {
229 (Ok(signature), Ok(verifying_key)) => {
230 verifying_key.verify_prehash(&m, &signature).is_ok()
231 },
232 _ => false,
233 }
234 },
235 _ => return Err(Error::NotSupported(None)),
236 };
237
238 Ok(result)
240}
241
242pub(crate) fn generate_key(
244 cx: &mut JSContext,
245 global: &GlobalScope,
246 normalized_algorithm: &SubtleEcKeyGenParams,
247 extractable: bool,
248 usages: Vec<KeyUsage>,
249) -> Result<CryptoKeyPair, Error> {
250 ec_common::generate_key(
251 EcAlgorithm::Ecdsa,
252 cx,
253 global,
254 normalized_algorithm,
255 extractable,
256 usages,
257 )
258}
259
260pub(crate) fn import_key(
262 cx: &mut JSContext,
263 global: &GlobalScope,
264 normalized_algorithm: &SubtleEcKeyImportParams,
265 format: KeyFormat,
266 key_data: &[u8],
267 extractable: bool,
268 usages: Vec<KeyUsage>,
269) -> Result<DomRoot<CryptoKey>, Error> {
270 ec_common::import_key(
271 EcAlgorithm::Ecdsa,
272 cx,
273 global,
274 normalized_algorithm,
275 format,
276 key_data,
277 extractable,
278 usages,
279 )
280}
281
282pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
284 ec_common::export_key(format, key)
285}
286
287pub(crate) fn get_public_key(
290 cx: &mut JSContext,
291 global: &GlobalScope,
292 key: &CryptoKey,
293 algorithm: &KeyAlgorithmAndDerivatives,
294 usages: Vec<KeyUsage>,
295) -> Result<DomRoot<CryptoKey>, Error> {
296 ec_common::get_public_key(cx, global, key, algorithm, usages)
297}
298
299fn expand_prehash(prehash: Vec<u8>, length: usize) -> Vec<u8> {
305 if prehash.len() < length {
306 let mut left_padded_prehash = vec![0u8; length];
307 left_padded_prehash[length - prehash.len()..].copy_from_slice(&prehash);
308 left_padded_prehash
309 } else {
310 prehash
311 }
312}