script/dom/webcrypto/subtlecrypto/
ecdh_operation.rs1use elliptic_curve::Curve;
6use elliptic_curve::array::typenum::Unsigned;
7use js::context::JSContext;
8use p256::NistP256;
9use p256::ecdh::diffie_hellman as p256_diffie_hellman;
10use p384::NistP384;
11use p384::ecdh::diffie_hellman as p384_diffie_hellman;
12use p521::NistP521;
13use p521::ecdh::diffie_hellman as p521_diffie_hellman;
14
15use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
16 CryptoKeyMethods, CryptoKeyPair, KeyType, KeyUsage,
17};
18use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::KeyFormat;
19use crate::dom::bindings::error::Error;
20use crate::dom::bindings::root::DomRoot;
21use crate::dom::cryptokey::{CryptoKey, Handle};
22use crate::dom::globalscope::GlobalScope;
23use crate::dom::subtlecrypto::ec_common::EcAlgorithm;
24use crate::dom::subtlecrypto::{
25 EcKeyGenParams, EcKeyImportParams, EcdhKeyDeriveParams, ExportedKey,
26 KeyAlgorithmAndDerivatives, NAMED_CURVE_P256, NAMED_CURVE_P384, NAMED_CURVE_P521, ec_common,
27};
28
29pub(crate) fn generate_key(
31 cx: &mut JSContext,
32 global: &GlobalScope,
33 normalized_algorithm: &EcKeyGenParams,
34 extractable: bool,
35 usages: Vec<KeyUsage>,
36) -> Result<CryptoKeyPair, Error> {
37 ec_common::generate_key(
38 EcAlgorithm::Ecdh,
39 cx,
40 global,
41 normalized_algorithm,
42 extractable,
43 usages,
44 )
45}
46
47pub(crate) fn derive_bits(
49 normalized_algorithm: &EcdhKeyDeriveParams,
50 key: &CryptoKey,
51 length: Option<u32>,
52) -> Result<Vec<u8>, Error> {
53 let public_key = normalized_algorithm.public.root();
55
56 if public_key.Type() != KeyType::Public {
59 return Err(Error::InvalidAccess(Some(
60 "[[type]] internal slot of publicKey is not \"public\"".into(),
61 )));
62 }
63
64 if public_key.algorithm().name() != normalized_algorithm.name {
67 return Err(Error::InvalidAccess(Some(
68 "The name attribute of the [[algorithm]] internal slot of publicKey does not match \
69 the name member of normalizedAlgorithm"
70 .into(),
71 )));
72 }
73
74 let maximum_length = maximum_length(&public_key)?;
78
79 if length.is_some_and(|length| length > maximum_length) {
82 return Err(Error::Operation(Some(
83 "Required length is greater than the maximum length supported by the elliptic curve"
84 .into(),
85 )));
86 }
87
88 if key.Type() != KeyType::Private {
91 return Err(Error::InvalidAccess(Some(
92 "[[type]] internal slot of key is not \"private\"".to_string(),
93 )));
94 }
95
96 if public_key.algorithm().name() != key.algorithm().name() {
100 return Err(Error::InvalidAccess(Some(
101 "public key [[algorithm]] internal slot name does not match that of private key"
102 .to_string(),
103 )));
104 }
105
106 let (
110 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(public_key_algorithm),
111 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(key_algorithm),
112 ) = (public_key.algorithm(), key.algorithm())
113 else {
114 return Err(Error::Operation(Some(
115 "Public or private key [[algorithm]] internal slot is not an elliptic curve algorithm"
116 .into(),
117 )));
118 };
119 if public_key_algorithm.named_curve != key_algorithm.named_curve {
120 return Err(Error::InvalidAccess(Some(
121 "Public and private keys [[algorithm]] internal slots namedCurves do not match".into(),
122 )));
123 }
124
125 let secret = match key_algorithm.named_curve.as_str() {
145 NAMED_CURVE_P256 => {
146 let Handle::P256PrivateKey(private_key) = key.handle() else {
147 return Err(Error::Operation(Some(
148 "Private key is not a P-256 private key".to_string(),
149 )));
150 };
151 let Handle::P256PublicKey(public_key) = public_key.handle() else {
152 return Err(Error::Operation(Some(
153 "Public key is not a P-256 public key".to_string(),
154 )));
155 };
156 p256_diffie_hellman(private_key.to_nonzero_scalar(), public_key.as_affine())
157 .raw_secret_bytes()
158 .to_vec()
159 },
160 NAMED_CURVE_P384 => {
161 let Handle::P384PrivateKey(private_key) = key.handle() else {
162 return Err(Error::Operation(Some(
163 "Private key is not a P-384 private key".to_string(),
164 )));
165 };
166 let Handle::P384PublicKey(public_key) = public_key.handle() else {
167 return Err(Error::Operation(Some(
168 "Public key is not a P384 public key".to_string(),
169 )));
170 };
171 p384_diffie_hellman(private_key.to_nonzero_scalar(), public_key.as_affine())
172 .raw_secret_bytes()
173 .to_vec()
174 },
175 NAMED_CURVE_P521 => {
176 let Handle::P521PrivateKey(private_key) = key.handle() else {
177 return Err(Error::Operation(Some(
178 "Private key is not a P-521 private key".to_string(),
179 )));
180 };
181 let Handle::P521PublicKey(public_key) = public_key.handle() else {
182 return Err(Error::Operation(Some(
183 "Public key is not a P-521 public key".to_string(),
184 )));
185 };
186 p521_diffie_hellman(private_key.to_nonzero_scalar(), public_key.as_affine())
187 .raw_secret_bytes()
188 .to_vec()
189 },
190 _ => {
191 return Err(Error::NotSupported(Some(format!(
192 "Unsupported namedCurve: {}",
193 key_algorithm.named_curve
194 ))));
195 },
196 };
197
198 match length {
207 None => Ok(secret),
208 Some(length) => {
209 if secret.len() * 8 < length as usize {
210 Err(Error::Operation(Some(
211 "Derived secret is too short".to_string(),
212 )))
213 } else {
214 let mut secret = secret[..length.div_ceil(8) as usize].to_vec();
215 if length % 8 != 0 {
216 let mask = u8::MAX << (8 - length % 8);
218 if let Some(last_byte) = secret.last_mut() {
219 *last_byte &= mask;
220 }
221 }
222 Ok(secret)
223 }
224 },
225 }
226}
227
228pub(crate) fn import_key(
230 cx: &mut JSContext,
231 global: &GlobalScope,
232 normalized_algorithm: &EcKeyImportParams,
233 format: KeyFormat,
234 key_data: &[u8],
235 extractable: bool,
236 usages: Vec<KeyUsage>,
237) -> Result<DomRoot<CryptoKey>, Error> {
238 ec_common::import_key(
239 EcAlgorithm::Ecdh,
240 cx,
241 global,
242 normalized_algorithm,
243 format,
244 key_data,
245 extractable,
246 usages,
247 )
248}
249
250pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
252 ec_common::export_key(format, key)
253}
254
255pub(crate) fn get_public_key(
258 cx: &mut JSContext,
259 global: &GlobalScope,
260 key: &CryptoKey,
261 algorithm: &KeyAlgorithmAndDerivatives,
262 usages: Vec<KeyUsage>,
263) -> Result<DomRoot<CryptoKey>, Error> {
264 ec_common::get_public_key(cx, global, key, algorithm, usages)
265}
266
267pub(crate) fn maximum_length(key: &CryptoKey) -> Result<u32, Error> {
271 let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = key.algorithm() else {
272 return Err(Error::Operation(Some(
273 "The key is not an elliptic curve algorithm key".to_string(),
274 )));
275 };
276
277 let maximum_length_in_bytes = match algorithm.named_curve.as_str() {
278 NAMED_CURVE_P256 => <NistP256 as Curve>::FieldBytesSize::to_u32(),
279 NAMED_CURVE_P384 => <NistP384 as Curve>::FieldBytesSize::to_u32(),
280 NAMED_CURVE_P521 => <NistP521 as Curve>::FieldBytesSize::to_u32(),
281 named_curve => {
282 return Err(Error::NotSupported(Some(format!(
283 "Unsupported namedCurve: {}",
284 named_curve
285 ))));
286 },
287 };
288
289 Ok(maximum_length_in_bytes * 8)
290}