script_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;
14use script_bindings::codegen::GenericBindings::CryptoKeyBinding::{
15 CryptoKeyPair, KeyType, KeyUsage,
16};
17use script_bindings::codegen::GenericBindings::SubtleCryptoBinding::KeyFormat;
18use script_bindings::error::Error;
19use script_bindings::root::DomRoot;
20
21use crate::cryptokey::{CryptoKey, Handle};
22use crate::subtlecrypto::ec_common::EcAlgorithm;
23use crate::subtlecrypto::{
24 EcKeyGenParams, EcKeyImportParams, EcdhKeyDeriveParams, ExportedKey,
25 KeyAlgorithmAndDerivatives, NAMED_CURVE_P256, NAMED_CURVE_P384, NAMED_CURVE_P521, ec_common,
26};
27use crate::traits::Equivalence;
28
29pub(crate) fn generate_key<D: Equivalence>(
31 cx: &mut JSContext,
32 global: &D::GlobalScope,
33 normalized_algorithm: &EcKeyGenParams,
34 extractable: bool,
35 usages: Vec<KeyUsage>,
36) -> Result<CryptoKeyPair<D>, Error> {
37 ec_common::generate_key(
38 cx,
39 global,
40 EcAlgorithm::Ecdh,
41 normalized_algorithm,
42 extractable,
43 usages,
44 )
45}
46
47pub(crate) fn derive_bits<D: Equivalence>(
49 normalized_algorithm: &EcdhKeyDeriveParams<D>,
50 key: &CryptoKey<D>,
51 length: Option<u32>,
52) -> Result<Vec<u8>, Error> {
53 let public_key = normalized_algorithm.public.root();
55
56 public_key.ensure_type(KeyType::Public)?;
59
60 if public_key.algorithm().name() != normalized_algorithm.name {
63 return Err(Error::InvalidAccess(Some(
64 "The name attribute of the [[algorithm]] internal slot of publicKey does not match \
65 the name member of normalizedAlgorithm"
66 .into(),
67 )));
68 }
69
70 let maximum_length = maximum_length(&public_key)?;
74
75 if length.is_some_and(|length| length > maximum_length) {
78 return Err(Error::Operation(Some(
79 "Required length is greater than the maximum length supported by the elliptic curve"
80 .into(),
81 )));
82 }
83
84 key.ensure_type(KeyType::Private)?;
87
88 if public_key.algorithm().name() != key.algorithm().name() {
92 return Err(Error::InvalidAccess(Some(
93 "public key [[algorithm]] internal slot name does not match that of private key"
94 .to_string(),
95 )));
96 }
97
98 let (
102 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(public_key_algorithm),
103 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(key_algorithm),
104 ) = (public_key.algorithm(), key.algorithm())
105 else {
106 return Err(Error::Operation(Some(
107 "Public or private key [[algorithm]] internal slot is not an elliptic curve algorithm"
108 .into(),
109 )));
110 };
111 if public_key_algorithm.named_curve != key_algorithm.named_curve {
112 return Err(Error::InvalidAccess(Some(
113 "Public and private keys [[algorithm]] internal slots namedCurves do not match".into(),
114 )));
115 }
116
117 let secret = match key_algorithm.named_curve.as_str() {
137 NAMED_CURVE_P256 => {
138 let Handle::P256PrivateKey(private_key) = key.handle() else {
139 return Err(Error::Operation(Some(
140 "Private key is not a P-256 private key".to_string(),
141 )));
142 };
143 let Handle::P256PublicKey(public_key) = public_key.handle() else {
144 return Err(Error::Operation(Some(
145 "Public key is not a P-256 public key".to_string(),
146 )));
147 };
148 p256_diffie_hellman(private_key.to_nonzero_scalar(), public_key.as_affine())
149 .raw_secret_bytes()
150 .to_vec()
151 },
152 NAMED_CURVE_P384 => {
153 let Handle::P384PrivateKey(private_key) = key.handle() else {
154 return Err(Error::Operation(Some(
155 "Private key is not a P-384 private key".to_string(),
156 )));
157 };
158 let Handle::P384PublicKey(public_key) = public_key.handle() else {
159 return Err(Error::Operation(Some(
160 "Public key is not a P384 public key".to_string(),
161 )));
162 };
163 p384_diffie_hellman(private_key.to_nonzero_scalar(), public_key.as_affine())
164 .raw_secret_bytes()
165 .to_vec()
166 },
167 NAMED_CURVE_P521 => {
168 let Handle::P521PrivateKey(private_key) = key.handle() else {
169 return Err(Error::Operation(Some(
170 "Private key is not a P-521 private key".to_string(),
171 )));
172 };
173 let Handle::P521PublicKey(public_key) = public_key.handle() else {
174 return Err(Error::Operation(Some(
175 "Public key is not a P-521 public key".to_string(),
176 )));
177 };
178 p521_diffie_hellman(private_key.to_nonzero_scalar(), public_key.as_affine())
179 .raw_secret_bytes()
180 .to_vec()
181 },
182 _ => {
183 return Err(Error::NotSupported(Some(format!(
184 "Unsupported namedCurve: {}",
185 key_algorithm.named_curve
186 ))));
187 },
188 };
189
190 match length {
199 None => Ok(secret),
200 Some(length) => {
201 if secret.len() * 8 < length as usize {
202 Err(Error::Operation(Some(
203 "Derived secret is too short".to_string(),
204 )))
205 } else {
206 let mut secret = secret[..length.div_ceil(8) as usize].to_vec();
207 if length % 8 != 0 {
208 let mask = u8::MAX << (8 - length % 8);
210 if let Some(last_byte) = secret.last_mut() {
211 *last_byte &= mask;
212 }
213 }
214 Ok(secret)
215 }
216 },
217 }
218}
219
220pub(crate) fn import_key<D: Equivalence>(
222 cx: &mut JSContext,
223 global: &D::GlobalScope,
224 normalized_algorithm: &EcKeyImportParams,
225 format: KeyFormat,
226 key_data: &[u8],
227 extractable: bool,
228 usages: Vec<KeyUsage>,
229) -> Result<DomRoot<CryptoKey<D>>, Error> {
230 ec_common::import_key(
231 cx,
232 global,
233 EcAlgorithm::Ecdh,
234 normalized_algorithm,
235 format,
236 key_data,
237 extractable,
238 usages,
239 )
240}
241
242pub(crate) fn export_key<D: Equivalence>(
244 format: KeyFormat,
245 key: &CryptoKey<D>,
246) -> Result<ExportedKey, Error> {
247 ec_common::export_key(format, key)
248}
249
250pub(crate) fn get_public_key<D: Equivalence>(
253 cx: &mut JSContext,
254 global: &D::GlobalScope,
255 key: &CryptoKey<D>,
256 algorithm: &KeyAlgorithmAndDerivatives,
257 usages: Vec<KeyUsage>,
258) -> Result<DomRoot<CryptoKey<D>>, Error> {
259 ec_common::get_public_key(cx, global, EcAlgorithm::Ecdh, key, algorithm, usages)
260}
261
262pub(crate) fn maximum_length<D: Equivalence>(key: &CryptoKey<D>) -> Result<u32, Error> {
266 let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = key.algorithm() else {
267 return Err(Error::Operation(Some(
268 "The key is not an elliptic curve algorithm key".to_string(),
269 )));
270 };
271
272 let maximum_length_in_bytes = match algorithm.named_curve.as_str() {
273 NAMED_CURVE_P256 => <NistP256 as Curve>::FieldBytesSize::to_u32(),
274 NAMED_CURVE_P384 => <NistP384 as Curve>::FieldBytesSize::to_u32(),
275 NAMED_CURVE_P521 => <NistP521 as Curve>::FieldBytesSize::to_u32(),
276 named_curve => {
277 return Err(Error::NotSupported(Some(format!(
278 "Unsupported namedCurve: {}",
279 named_curve
280 ))));
281 },
282 };
283
284 Ok(maximum_length_in_bytes * 8)
285}