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::digest(message).to_vec(),
54 CryptoAlgorithm::Sha256 => Sha256::digest(message).to_vec(),
55 CryptoAlgorithm::Sha384 => Sha384::digest(message).to_vec(),
56 CryptoAlgorithm::Sha512 => Sha512::digest(message).to_vec(),
57 hash_algorithm_name => {
58 return Err(Error::NotSupported(Some(format!(
59 "Unsupported hash algorithm for ECDSA: {}",
60 hash_algorithm_name.as_str()
61 ))));
62 },
63 };
64
65 let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = key.algorithm() else {
86 return Err(Error::Operation(None));
87 };
88 let result = match algorithm.named_curve.as_str() {
89 NAMED_CURVE_P256 => {
90 let m = expand_prehash(m, P256_PREHASH_LENGTH);
93
94 let Handle::P256PrivateKey(d) = key.handle() else {
95 return Err(Error::Operation(None));
96 };
97 let signing_key = SigningKey::<NistP256>::from(d);
98 let signature: Signature<NistP256> = signing_key
99 .sign_prehash_with_rng(&mut OsRng, m.as_slice())
100 .map_err(|_| Error::Operation(None))?;
101 signature.to_vec()
102 },
103 NAMED_CURVE_P384 => {
104 let m = expand_prehash(m, P384_PREHASH_LENGTH);
107
108 let Handle::P384PrivateKey(d) = key.handle() else {
109 return Err(Error::Operation(None));
110 };
111 let signing_key = SigningKey::<NistP384>::from(d);
112 let signature: Signature<NistP384> = signing_key
113 .sign_prehash_with_rng(&mut OsRng, m.as_slice())
114 .map_err(|_| Error::Abort(None))?;
115 signature.to_vec()
116 },
117 NAMED_CURVE_P521 => {
118 let m = expand_prehash(m, P521_PREHASH_LENGTH);
121
122 let Handle::P521PrivateKey(d) = key.handle() else {
123 return Err(Error::Operation(None));
124 };
125 let signing_key = p521::ecdsa::SigningKey::from_slice(d.to_bytes().as_slice())
126 .map_err(|_| Error::Operation(None))?;
127 let signature: Signature<NistP521> = signing_key
128 .sign_prehash_with_rng(&mut OsRng, m.as_slice())
129 .map_err(|_| Error::Operation(None))?;
130 signature.to_vec()
131 },
132 _ => return Err(Error::NotSupported(None)),
133 };
134
135 Ok(result)
137}
138
139pub(crate) fn verify(
141 normalized_algorithm: &SubtleEcdsaParams,
142 key: &CryptoKey,
143 message: &[u8],
144 signature: &[u8],
145) -> Result<bool, Error> {
146 if key.Type() != KeyType::Public {
149 return Err(Error::InvalidAccess(None));
150 }
151
152 let hash_algorithm = &normalized_algorithm.hash;
154
155 let m = match hash_algorithm.name() {
158 CryptoAlgorithm::Sha1 => Sha1::new_with_prefix(message).finalize().to_vec(),
159 CryptoAlgorithm::Sha256 => Sha256::new_with_prefix(message).finalize().to_vec(),
160 CryptoAlgorithm::Sha384 => Sha384::new_with_prefix(message).finalize().to_vec(),
161 CryptoAlgorithm::Sha512 => Sha512::new_with_prefix(message).finalize().to_vec(),
162 _ => return Err(Error::NotSupported(None)),
163 };
164
165 let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = key.algorithm() else {
187 return Err(Error::Operation(None));
188 };
189 let result = match algorithm.named_curve.as_str() {
190 NAMED_CURVE_P256 => {
191 let m = expand_prehash(m, P256_PREHASH_LENGTH);
194
195 let Handle::P256PublicKey(q) = key.handle() else {
196 return Err(Error::Operation(None));
197 };
198 match Signature::<NistP256>::from_slice(signature) {
199 Ok(signature) => {
200 let verifying_key = VerifyingKey::<NistP256>::from(q);
201 verifying_key.verify_prehash(&m, &signature).is_ok()
202 },
203 Err(_) => false,
204 }
205 },
206 NAMED_CURVE_P384 => {
207 let m = expand_prehash(m, P384_PREHASH_LENGTH);
210
211 let Handle::P384PublicKey(q) = key.handle() else {
212 return Err(Error::Operation(None));
213 };
214 match Signature::<NistP384>::from_slice(signature) {
215 Ok(signature) => {
216 let verifying_key = VerifyingKey::<NistP384>::from(q);
217 verifying_key.verify_prehash(&m, &signature).is_ok()
218 },
219 Err(_) => false,
220 }
221 },
222 NAMED_CURVE_P521 => {
223 let m = expand_prehash(m, P521_PREHASH_LENGTH);
226
227 let Handle::P521PublicKey(q) = key.handle() else {
228 return Err(Error::Operation(None));
229 };
230 match (
231 Signature::<NistP521>::from_slice(signature),
232 p521::ecdsa::VerifyingKey::from_sec1_bytes(q.to_sec1_bytes().to_vec().as_slice()),
233 ) {
234 (Ok(signature), Ok(verifying_key)) => {
235 verifying_key.verify_prehash(&m, &signature).is_ok()
236 },
237 _ => false,
238 }
239 },
240 _ => return Err(Error::NotSupported(None)),
241 };
242
243 Ok(result)
245}
246
247pub(crate) fn generate_key(
249 cx: &mut JSContext,
250 global: &GlobalScope,
251 normalized_algorithm: &SubtleEcKeyGenParams,
252 extractable: bool,
253 usages: Vec<KeyUsage>,
254) -> Result<CryptoKeyPair, Error> {
255 ec_common::generate_key(
256 EcAlgorithm::Ecdsa,
257 cx,
258 global,
259 normalized_algorithm,
260 extractable,
261 usages,
262 )
263}
264
265pub(crate) fn import_key(
267 cx: &mut JSContext,
268 global: &GlobalScope,
269 normalized_algorithm: &SubtleEcKeyImportParams,
270 format: KeyFormat,
271 key_data: &[u8],
272 extractable: bool,
273 usages: Vec<KeyUsage>,
274) -> Result<DomRoot<CryptoKey>, Error> {
275 ec_common::import_key(
276 EcAlgorithm::Ecdsa,
277 cx,
278 global,
279 normalized_algorithm,
280 format,
281 key_data,
282 extractable,
283 usages,
284 )
285}
286
287pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
289 ec_common::export_key(format, key)
290}
291
292pub(crate) fn get_public_key(
295 cx: &mut JSContext,
296 global: &GlobalScope,
297 key: &CryptoKey,
298 algorithm: &KeyAlgorithmAndDerivatives,
299 usages: Vec<KeyUsage>,
300) -> Result<DomRoot<CryptoKey>, Error> {
301 ec_common::get_public_key(cx, global, key, algorithm, usages)
302}
303
304fn expand_prehash(prehash: Vec<u8>, length: usize) -> Vec<u8> {
310 if prehash.len() < length {
311 let mut left_padded_prehash = vec![0u8; length];
312 left_padded_prehash[length - prehash.len()..].copy_from_slice(&prehash);
313 left_padded_prehash
314 } else {
315 prehash
316 }
317}