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 ExportedKey, KeyAlgorithmAndDerivatives, NAMED_CURVE_P256, NAMED_CURVE_P384, NAMED_CURVE_P521,
26 SubtleEcKeyGenParams, SubtleEcKeyImportParams, SubtleEcdhKeyDeriveParams, ec_common,
27};
28
29pub(crate) fn generate_key(
31 cx: &mut JSContext,
32 global: &GlobalScope,
33 normalized_algorithm: &SubtleEcKeyGenParams,
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: &SubtleEcdhKeyDeriveParams,
50 key: &CryptoKey,
51 length: Option<u32>,
52) -> Result<Vec<u8>, Error> {
53 if key.Type() != KeyType::Private {
56 return Err(Error::InvalidAccess(Some(
57 "[[type]] internal slot of key is not \"private\"".to_string(),
58 )));
59 }
60
61 let public_key = normalized_algorithm.public.root();
63
64 if public_key.Type() != KeyType::Public {
67 return Err(Error::InvalidAccess(Some(
68 "[[type]] internal slot of key is not \"public\"".to_string(),
69 )));
70 }
71
72 if public_key.algorithm().name() != key.algorithm().name() {
76 return Err(Error::InvalidAccess(Some(
77 "public key [[algorithm]] internal slot name does not match that of private key"
78 .to_string(),
79 )));
80 }
81
82 let (
86 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(public_key_algorithm),
87 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(key_algorithm),
88 ) = (public_key.algorithm(), key.algorithm())
89 else {
90 return Err(Error::Operation(Some("Public or private key's [[algorithm]] internal slot is not an elliptic curve algorithm".to_string())));
91 };
92 if public_key_algorithm.named_curve != key_algorithm.named_curve {
93 return Err(Error::InvalidAccess(Some(
94 "Public and private keys' [[algorithm]] internal slots namedCurves do not match"
95 .to_string(),
96 )));
97 }
98
99 let secret = match key_algorithm.named_curve.as_str() {
119 NAMED_CURVE_P256 => {
120 let Handle::P256PrivateKey(private_key) = key.handle() else {
121 return Err(Error::Operation(Some(
122 "Private key is not a P-256 private key".to_string(),
123 )));
124 };
125 let Handle::P256PublicKey(public_key) = public_key.handle() else {
126 return Err(Error::Operation(Some(
127 "Public key is not a P-256 public key".to_string(),
128 )));
129 };
130 p256_diffie_hellman(private_key.to_nonzero_scalar(), public_key.as_affine())
131 .raw_secret_bytes()
132 .to_vec()
133 },
134 NAMED_CURVE_P384 => {
135 let Handle::P384PrivateKey(private_key) = key.handle() else {
136 return Err(Error::Operation(Some(
137 "Private key is not a P-384 private key".to_string(),
138 )));
139 };
140 let Handle::P384PublicKey(public_key) = public_key.handle() else {
141 return Err(Error::Operation(Some(
142 "Public key is not a P384 public key".to_string(),
143 )));
144 };
145 p384_diffie_hellman(private_key.to_nonzero_scalar(), public_key.as_affine())
146 .raw_secret_bytes()
147 .to_vec()
148 },
149 NAMED_CURVE_P521 => {
150 let Handle::P521PrivateKey(private_key) = key.handle() else {
151 return Err(Error::Operation(Some(
152 "Private key is not a P-521 private key".to_string(),
153 )));
154 };
155 let Handle::P521PublicKey(public_key) = public_key.handle() else {
156 return Err(Error::Operation(Some(
157 "Public key is not a P-521 public key".to_string(),
158 )));
159 };
160 p521_diffie_hellman(private_key.to_nonzero_scalar(), public_key.as_affine())
161 .raw_secret_bytes()
162 .to_vec()
163 },
164 _ => {
165 return Err(Error::NotSupported(Some(format!(
166 "Unsupported namedCurve: {}",
167 key_algorithm.named_curve
168 ))));
169 },
170 };
171
172 match length {
181 None => Ok(secret),
182 Some(length) => {
183 if secret.len() * 8 < length as usize {
184 Err(Error::Operation(Some(
185 "Derived secret is too short".to_string(),
186 )))
187 } else {
188 let mut secret = secret[..length.div_ceil(8) as usize].to_vec();
189 if length % 8 != 0 {
190 let mask = u8::MAX << (8 - length % 8);
192 if let Some(last_byte) = secret.last_mut() {
193 *last_byte &= mask;
194 }
195 }
196 Ok(secret)
197 }
198 },
199 }
200}
201
202pub(crate) fn import_key(
204 cx: &mut JSContext,
205 global: &GlobalScope,
206 normalized_algorithm: &SubtleEcKeyImportParams,
207 format: KeyFormat,
208 key_data: &[u8],
209 extractable: bool,
210 usages: Vec<KeyUsage>,
211) -> Result<DomRoot<CryptoKey>, Error> {
212 ec_common::import_key(
213 EcAlgorithm::Ecdh,
214 cx,
215 global,
216 normalized_algorithm,
217 format,
218 key_data,
219 extractable,
220 usages,
221 )
222}
223
224pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
226 ec_common::export_key(format, key)
227}
228
229pub(crate) fn get_public_key(
232 cx: &mut JSContext,
233 global: &GlobalScope,
234 key: &CryptoKey,
235 algorithm: &KeyAlgorithmAndDerivatives,
236 usages: Vec<KeyUsage>,
237) -> Result<DomRoot<CryptoKey>, Error> {
238 ec_common::get_public_key(cx, global, key, algorithm, usages)
239}
240
241pub(crate) fn secret_length(
245 normalized_algorithm: &SubtleEcdhKeyDeriveParams,
246) -> Result<u32, Error> {
247 let public_key = normalized_algorithm.public.root();
248 let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = public_key.algorithm() else {
249 return Err(Error::Operation(Some(
250 "The key is not an elliptic curve algorithm key".to_string(),
251 )));
252 };
253
254 let secret_length_in_bits = match algorithm.named_curve.as_str() {
255 NAMED_CURVE_P256 => <NistP256 as Curve>::FieldBytesSize::to_u32(),
256 NAMED_CURVE_P384 => <NistP384 as Curve>::FieldBytesSize::to_u32(),
257 NAMED_CURVE_P521 => <NistP521 as Curve>::FieldBytesSize::to_u32(),
258 named_curve => {
259 return Err(Error::NotSupported(Some(format!(
260 "Unsupported namedCurve: {}",
261 named_curve
262 ))));
263 },
264 };
265
266 Ok(secret_length_in_bits)
267}