script/dom/webcrypto/subtlecrypto/
ecdh_operation.rs1use elliptic_curve::Curve;
6use elliptic_curve::generic_array::typenum::Unsigned;
7use js::context::JSContext;
8use p256::NistP256;
9use p384::NistP384;
10use p521::NistP521;
11
12use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
13 CryptoKeyMethods, CryptoKeyPair, KeyType, KeyUsage,
14};
15use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::KeyFormat;
16use crate::dom::bindings::error::Error;
17use crate::dom::bindings::root::DomRoot;
18use crate::dom::cryptokey::{CryptoKey, Handle};
19use crate::dom::globalscope::GlobalScope;
20use crate::dom::subtlecrypto::ec_common::EcAlgorithm;
21use crate::dom::subtlecrypto::{
22 ExportedKey, KeyAlgorithmAndDerivatives, NAMED_CURVE_P256, NAMED_CURVE_P384, NAMED_CURVE_P521,
23 SubtleEcKeyGenParams, SubtleEcKeyImportParams, SubtleEcdhKeyDeriveParams, ec_common,
24};
25
26pub(crate) fn generate_key(
28 cx: &mut JSContext,
29 global: &GlobalScope,
30 normalized_algorithm: &SubtleEcKeyGenParams,
31 extractable: bool,
32 usages: Vec<KeyUsage>,
33) -> Result<CryptoKeyPair, Error> {
34 ec_common::generate_key(
35 EcAlgorithm::Ecdh,
36 cx,
37 global,
38 normalized_algorithm,
39 extractable,
40 usages,
41 )
42}
43
44pub(crate) fn derive_bits(
46 normalized_algorithm: &SubtleEcdhKeyDeriveParams,
47 key: &CryptoKey,
48 length: Option<u32>,
49) -> Result<Vec<u8>, Error> {
50 if key.Type() != KeyType::Private {
53 return Err(Error::InvalidAccess(Some(
54 "[[type]] internal slot of key is not \"private\"".to_string(),
55 )));
56 }
57
58 let public_key = normalized_algorithm.public.root();
60
61 if public_key.Type() != KeyType::Public {
64 return Err(Error::InvalidAccess(Some(
65 "[[type]] internal slot of key is not \"public\"".to_string(),
66 )));
67 }
68
69 if public_key.algorithm().name() != key.algorithm().name() {
73 return Err(Error::InvalidAccess(Some(
74 "public key [[algorithm]] internal slot name does not match that of private key"
75 .to_string(),
76 )));
77 }
78
79 let (
83 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(public_key_algorithm),
84 KeyAlgorithmAndDerivatives::EcKeyAlgorithm(key_algorithm),
85 ) = (public_key.algorithm(), key.algorithm())
86 else {
87 return Err(Error::Operation(Some("Public or private key's [[algorithm]] internal slot is not an elliptic curve algorithm".to_string())));
88 };
89 if public_key_algorithm.named_curve != key_algorithm.named_curve {
90 return Err(Error::InvalidAccess(Some(
91 "Public and private keys' [[algorithm]] internal slots namedCurves do not match"
92 .to_string(),
93 )));
94 }
95
96 let secret = match key_algorithm.named_curve.as_str() {
116 NAMED_CURVE_P256 => {
117 let Handle::P256PrivateKey(private_key) = key.handle() else {
118 return Err(Error::Operation(Some(
119 "Private key is not a P-256 private key".to_string(),
120 )));
121 };
122 let Handle::P256PublicKey(public_key) = public_key.handle() else {
123 return Err(Error::Operation(Some(
124 "Public key is not a P-256 public key".to_string(),
125 )));
126 };
127 p256::ecdh::diffie_hellman(private_key.to_nonzero_scalar(), public_key.as_affine())
128 .raw_secret_bytes()
129 .to_vec()
130 },
131 NAMED_CURVE_P384 => {
132 let Handle::P384PrivateKey(private_key) = key.handle() else {
133 return Err(Error::Operation(Some(
134 "Private key is not a P-384 private key".to_string(),
135 )));
136 };
137 let Handle::P384PublicKey(public_key) = public_key.handle() else {
138 return Err(Error::Operation(Some(
139 "Public key is not a P384 public key".to_string(),
140 )));
141 };
142 p384::ecdh::diffie_hellman(private_key.to_nonzero_scalar(), public_key.as_affine())
143 .raw_secret_bytes()
144 .to_vec()
145 },
146 NAMED_CURVE_P521 => {
147 let Handle::P521PrivateKey(private_key) = key.handle() else {
148 return Err(Error::Operation(Some(
149 "Private key is not a P-521 private key".to_string(),
150 )));
151 };
152 let Handle::P521PublicKey(public_key) = public_key.handle() else {
153 return Err(Error::Operation(Some(
154 "Public key is not a P-521 public key".to_string(),
155 )));
156 };
157 p521::ecdh::diffie_hellman(private_key.to_nonzero_scalar(), public_key.as_affine())
158 .raw_secret_bytes()
159 .to_vec()
160 },
161 _ => {
162 return Err(Error::NotSupported(Some(format!(
163 "Unsupported namedCurve: {}",
164 key_algorithm.named_curve
165 ))));
166 },
167 };
168
169 match length {
178 None => Ok(secret),
179 Some(length) => {
180 if secret.len() * 8 < length as usize {
181 Err(Error::Operation(Some(
182 "Derived secret is too short".to_string(),
183 )))
184 } else {
185 let mut secret = secret[..length.div_ceil(8) as usize].to_vec();
186 if length % 8 != 0 {
187 let mask = u8::MAX << (8 - length % 8);
189 if let Some(last_byte) = secret.last_mut() {
190 *last_byte &= mask;
191 }
192 }
193 Ok(secret)
194 }
195 },
196 }
197}
198
199pub(crate) fn import_key(
201 cx: &mut JSContext,
202 global: &GlobalScope,
203 normalized_algorithm: &SubtleEcKeyImportParams,
204 format: KeyFormat,
205 key_data: &[u8],
206 extractable: bool,
207 usages: Vec<KeyUsage>,
208) -> Result<DomRoot<CryptoKey>, Error> {
209 ec_common::import_key(
210 EcAlgorithm::Ecdh,
211 cx,
212 global,
213 normalized_algorithm,
214 format,
215 key_data,
216 extractable,
217 usages,
218 )
219}
220
221pub(crate) fn export_key(format: KeyFormat, key: &CryptoKey) -> Result<ExportedKey, Error> {
223 ec_common::export_key(format, key)
224}
225
226pub(crate) fn get_public_key(
229 cx: &mut JSContext,
230 global: &GlobalScope,
231 key: &CryptoKey,
232 algorithm: &KeyAlgorithmAndDerivatives,
233 usages: Vec<KeyUsage>,
234) -> Result<DomRoot<CryptoKey>, Error> {
235 ec_common::get_public_key(cx, global, key, algorithm, usages)
236}
237
238pub(crate) fn secret_length(
242 normalized_algorithm: &SubtleEcdhKeyDeriveParams,
243) -> Result<u32, Error> {
244 let public_key = normalized_algorithm.public.root();
245 let KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algorithm) = public_key.algorithm() else {
246 return Err(Error::Operation(Some(
247 "The key is not an elliptic curve algorithm key".to_string(),
248 )));
249 };
250
251 let secret_length_in_bits = match algorithm.named_curve.as_str() {
252 NAMED_CURVE_P256 => <NistP256 as Curve>::FieldBytesSize::to_u32(),
253 NAMED_CURVE_P384 => <NistP384 as Curve>::FieldBytesSize::to_u32(),
254 NAMED_CURVE_P521 => <NistP521 as Curve>::FieldBytesSize::to_u32(),
255 named_curve => {
256 return Err(Error::NotSupported(Some(format!(
257 "Unsupported namedCurve: {}",
258 named_curve
259 ))));
260 },
261 };
262
263 Ok(secret_length_in_bits)
264}