Skip to content

Commit 8494b56

Browse files
authored
script: Implement import key operation for ECDH (servo#40253)
Implement import key operation for ECDH, using cryptographic calculation implementation from the crates `p256`, `p384` and `p521`. Testing: - Pass some WPT tests that were expected to fail. - Some FAIL expectations are added. They are related to the not-yet-implemented "derive bits" operation of ECDH. WPT skipped them when "import key" operation for ECDH were not yet implemented. Fixes: Part of servo#39060 --------- Signed-off-by: Kingsley Yung <[email protected]>
1 parent 9fe53e5 commit 8494b56

16 files changed

+1066
-4177
lines changed

Cargo.lock

Lines changed: 237 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ data-url = "0.3"
5757
devtools_traits = { path = "components/shared/devtools" }
5858
dpi = "0.1"
5959
dwrote = "0.11.5"
60+
elliptic-curve = "0.13"
6061
embedder_traits = { path = "components/shared/embedder" }
6162
encoding_rs = "0.8"
6263
env_logger = "0.11"
@@ -110,9 +111,13 @@ nom-rfc8288 = "0.4.0"
110111
num-traits = "0.2"
111112
num_cpus = "1.17.0"
112113
openxr = "0.19"
114+
p256 = "0.13"
115+
p384 = "0.13"
116+
p521 = "0.13"
113117
parking_lot = "0.12"
114118
peniko = "0.5"
115119
percent-encoding = "2.3"
120+
pkcs8 = "0.10"
116121
proc-macro2 = "1"
117122
profile_traits = { path = "components/shared/profile" }
118123
quote = "1"
@@ -127,6 +132,7 @@ rustls = { version = "0.23", default-features = false, features = ["logging", "s
127132
rustls-pemfile = "2.0"
128133
rustls-pki-types = "1.13"
129134
script_traits = { path = "components/shared/script" }
135+
sec1 = "0.7"
130136
selectors = { git = "https://github.com/servo/stylo", branch = "2025-10-01" }
131137
serde = "1.0.228"
132138
serde_bytes = "0.11"

components/script/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ deny_public_fields = { path = "../deny_public_fields" }
6262
devtools_traits = { workspace = true }
6363
dom_struct = { path = "../dom_struct" }
6464
domobject_derive = { path = "../domobject_derive" }
65+
elliptic-curve = { workspace = true }
6566
embedder_traits = { workspace = true }
6667
encoding_rs = { workspace = true }
6768
euclid = { workspace = true }
@@ -96,17 +97,22 @@ nom = { workspace = true }
9697
nom-rfc8288 = { workspace = true }
9798
num-traits = { workspace = true }
9899
num_cpus = { workspace = true }
100+
p256 = { workspace = true }
101+
p384 = { workspace = true }
102+
p521 = { workspace = true }
99103
parking_lot = { workspace = true }
100104
percent-encoding = { workspace = true }
101105
phf = "0.11"
102106
pixels = { path = "../pixels" }
107+
pkcs8 = { workspace = true }
103108
profile_traits = { workspace = true }
104109
rand = { workspace = true }
105110
range = { path = "../range" }
106111
regex = { workspace = true }
107112
rustc-hash = { workspace = true }
108113
script_bindings = { path = "../script_bindings" }
109114
script_traits = { workspace = true }
115+
sec1 = { workspace = true }
110116
selectors = { workspace = true }
111117
serde = { workspace = true, features = ["derive"] }
112118
serde_json = { workspace = true }

components/script/dom/cryptokey.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::ptr::NonNull;
77

88
use dom_struct::dom_struct;
99
use js::jsapi::{Heap, JSObject, Value};
10+
use malloc_size_of::MallocSizeOf;
1011
use script_bindings::conversions::SafeToJSValConvertible;
1112

1213
use crate::dom::bindings::cell::DomRefCell;
@@ -26,8 +27,13 @@ pub(crate) enum CryptoKeyOrCryptoKeyPair {
2627

2728
/// The underlying cryptographic data this key represents
2829
#[allow(dead_code)]
29-
#[derive(MallocSizeOf)]
3030
pub(crate) enum Handle {
31+
P256PrivateKey(p256::SecretKey),
32+
P384PrivateKey(p384::SecretKey),
33+
P521PrivateKey(p521::SecretKey),
34+
P256PublicKey(p256::PublicKey),
35+
P384PublicKey(p384::PublicKey),
36+
P521PublicKey(p521::PublicKey),
3137
Aes128(Vec<u8>),
3238
Aes192(Vec<u8>),
3339
Aes256(Vec<u8>),
@@ -199,6 +205,27 @@ impl Handle {
199205
Self::Hkdf(bytes) => bytes,
200206
Self::Hmac(bytes) => bytes,
201207
Self::Ed25519(bytes) => bytes,
208+
_ => unreachable!(),
209+
}
210+
}
211+
}
212+
213+
impl MallocSizeOf for Handle {
214+
fn size_of(&self, ops: &mut malloc_size_of::MallocSizeOfOps) -> usize {
215+
match self {
216+
Handle::P256PrivateKey(secret_key) => secret_key.size_of(ops),
217+
Handle::P384PrivateKey(secret_key) => secret_key.size_of(ops),
218+
Handle::P521PrivateKey(secret_key) => secret_key.size_of(ops),
219+
Handle::P256PublicKey(public_key) => public_key.size_of(ops),
220+
Handle::P384PublicKey(public_key) => public_key.size_of(ops),
221+
Handle::P521PublicKey(public_key) => public_key.size_of(ops),
222+
Handle::Aes128(bytes) => bytes.size_of(ops),
223+
Handle::Aes192(bytes) => bytes.size_of(ops),
224+
Handle::Aes256(bytes) => bytes.size_of(ops),
225+
Handle::Pbkdf2(bytes) => bytes.size_of(ops),
226+
Handle::Hkdf(bytes) => bytes.size_of(ops),
227+
Handle::Hmac(bytes) => bytes.size_of(ops),
228+
Handle::Ed25519(bytes) => bytes.size_of(ops),
202229
}
203230
}
204231
}

components/script/dom/subtlecrypto.rs

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
44

55
mod aes_operation;
6+
mod ecdh_operation;
67
mod ed25519_operation;
78
mod hkdf_operation;
89
mod hmac_operation;
@@ -27,9 +28,9 @@ use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
2728
};
2829
use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{
2930
AesCbcParams, AesCtrParams, AesDerivedKeyParams, AesGcmParams, AesKeyAlgorithm,
30-
AesKeyGenParams, Algorithm, AlgorithmIdentifier, HkdfParams, HmacImportParams,
31-
HmacKeyAlgorithm, HmacKeyGenParams, JsonWebKey, KeyAlgorithm, KeyFormat, Pbkdf2Params,
32-
RsaOtherPrimesInfo, SubtleCryptoMethods,
31+
AesKeyGenParams, Algorithm, AlgorithmIdentifier, EcKeyAlgorithm, EcKeyImportParams, HkdfParams,
32+
HmacImportParams, HmacKeyAlgorithm, HmacKeyGenParams, JsonWebKey, KeyAlgorithm, KeyFormat,
33+
Pbkdf2Params, RsaOtherPrimesInfo, SubtleCryptoMethods,
3334
};
3435
use crate::dom::bindings::codegen::UnionTypes::{
3536
ArrayBufferViewOrArrayBuffer, ArrayBufferViewOrArrayBufferOrJsonWebKey, ObjectOrString,
@@ -89,7 +90,7 @@ static SUPPORTED_ALGORITHMS: &[&str] = &[
8990
const NAMED_CURVE_P256: &str = "P-256";
9091
const NAMED_CURVE_P384: &str = "P-384";
9192
const NAMED_CURVE_P521: &str = "P-521";
92-
#[allow(dead_code)]
93+
9394
static SUPPORTED_CURVES: &[&str] = &[NAMED_CURVE_P256, NAMED_CURVE_P384, NAMED_CURVE_P521];
9495

9596
/// <https://w3c.github.io/webcrypto/#supported-operation>
@@ -1625,6 +1626,48 @@ impl SafeToJSValConvertible for SubtleKeyAlgorithm {
16251626
}
16261627
}
16271628

1629+
/// <https://w3c.github.io/webcrypto/#dfn-EcKeyAlgorithm>
1630+
#[derive(Clone, Debug, MallocSizeOf)]
1631+
pub(crate) struct SubtleEcKeyAlgorithm {
1632+
/// <https://w3c.github.io/webcrypto/#dom-keyalgorithm-name>
1633+
name: String,
1634+
1635+
/// <https://w3c.github.io/webcrypto/#dfn-EcKeyAlgorithm-namedCurve>
1636+
named_curve: String,
1637+
}
1638+
1639+
impl SafeToJSValConvertible for SubtleEcKeyAlgorithm {
1640+
fn safe_to_jsval(&self, cx: JSContext, rval: MutableHandleValue, can_gc: CanGc) {
1641+
let parent = KeyAlgorithm {
1642+
name: self.name.clone().into(),
1643+
};
1644+
let dictionary = EcKeyAlgorithm {
1645+
parent,
1646+
namedCurve: self.named_curve.clone().into(),
1647+
};
1648+
dictionary.safe_to_jsval(cx, rval, can_gc);
1649+
}
1650+
}
1651+
1652+
/// <https://w3c.github.io/webcrypto/#dfn-EcKeyImportParams>
1653+
#[derive(Clone, Debug, MallocSizeOf)]
1654+
struct SubtleEcKeyImportParams {
1655+
/// <https://w3c.github.io/webcrypto/#dom-algorithm-name>
1656+
name: String,
1657+
1658+
/// <https://w3c.github.io/webcrypto/#dfn-EcKeyImportParams-namedCurve>
1659+
named_curve: String,
1660+
}
1661+
1662+
impl From<EcKeyImportParams> for SubtleEcKeyImportParams {
1663+
fn from(value: EcKeyImportParams) -> Self {
1664+
SubtleEcKeyImportParams {
1665+
name: value.parent.name.to_string(),
1666+
named_curve: value.namedCurve.to_string(),
1667+
}
1668+
}
1669+
}
1670+
16281671
#[derive(Clone, Debug, MallocSizeOf)]
16291672
pub(crate) struct SubtleAesCbcParams {
16301673
pub(crate) name: String,
@@ -1936,6 +1979,7 @@ pub(crate) enum ExportedKey {
19361979
#[allow(clippy::enum_variant_names)]
19371980
pub(crate) enum KeyAlgorithmAndDerivatives {
19381981
KeyAlgorithm(SubtleKeyAlgorithm),
1982+
EcKeyAlgorithm(SubtleEcKeyAlgorithm),
19391983
AesKeyAlgorithm(SubtleAesKeyAlgorithm),
19401984
HmacKeyAlgorithm(SubtleHmacKeyAlgorithm),
19411985
}
@@ -1944,6 +1988,7 @@ impl KeyAlgorithmAndDerivatives {
19441988
fn name(&self) -> &str {
19451989
match self {
19461990
KeyAlgorithmAndDerivatives::KeyAlgorithm(algo) => &algo.name,
1991+
KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algo) => &algo.name,
19471992
KeyAlgorithmAndDerivatives::AesKeyAlgorithm(algo) => &algo.name,
19481993
KeyAlgorithmAndDerivatives::HmacKeyAlgorithm(algo) => &algo.name,
19491994
}
@@ -1962,6 +2007,9 @@ impl SafeToJSValConvertible for KeyAlgorithmAndDerivatives {
19622007
fn safe_to_jsval(&self, cx: JSContext, rval: MutableHandleValue, can_gc: CanGc) {
19632008
match self {
19642009
KeyAlgorithmAndDerivatives::KeyAlgorithm(algo) => algo.safe_to_jsval(cx, rval, can_gc),
2010+
KeyAlgorithmAndDerivatives::EcKeyAlgorithm(algo) => {
2011+
algo.safe_to_jsval(cx, rval, can_gc)
2012+
},
19652013
KeyAlgorithmAndDerivatives::AesKeyAlgorithm(algo) => {
19662014
algo.safe_to_jsval(cx, rval, can_gc)
19672015
},
@@ -2129,6 +2177,7 @@ impl JsonWebKeyExt for JsonWebKey {
21292177
#[derive(Clone, Debug, MallocSizeOf)]
21302178
enum NormalizedAlgorithm {
21312179
Algorithm(SubtleAlgorithm),
2180+
EcKeyImportParams(SubtleEcKeyImportParams),
21322181
AesCtrParams(SubtleAesCtrParams),
21332182
AesKeyGenParams(SubtleAesKeyGenParams),
21342183
AesDerivedKeyParams(SubtleAesDerivedKeyParams),
@@ -2223,6 +2272,14 @@ fn normalize_algorithm(
22232272
// NOTE: Step 10.1.3 is done by the `From` and `TryFrom` trait implementation of
22242273
// "subtle" binding structs.
22252274
let normalized_algorithm = match (alg_name, op) {
2275+
// <https://w3c.github.io/webcrypto/#ecdh-registration>
2276+
(ALG_ECDH, Operation::ImportKey) => {
2277+
let mut params =
2278+
dictionary_from_jsval::<EcKeyImportParams>(cx, value.handle())?;
2279+
params.parent.name = DOMString::from(alg_name);
2280+
NormalizedAlgorithm::EcKeyImportParams(params.into())
2281+
},
2282+
22262283
// <https://w3c.github.io/webcrypto/#ed25519-registration>
22272284
(ALG_ED25519, Operation::Sign) => {
22282285
let mut params = dictionary_from_jsval::<Algorithm>(cx, value.handle())?;
@@ -2517,6 +2574,7 @@ impl NormalizedAlgorithm {
25172574
fn name(&self) -> &str {
25182575
match self {
25192576
NormalizedAlgorithm::Algorithm(algo) => &algo.name,
2577+
NormalizedAlgorithm::EcKeyImportParams(algo) => &algo.name,
25202578
NormalizedAlgorithm::AesCtrParams(algo) => &algo.name,
25212579
NormalizedAlgorithm::AesKeyGenParams(algo) => &algo.name,
25222580
NormalizedAlgorithm::AesDerivedKeyParams(algo) => &algo.name,
@@ -2702,6 +2760,15 @@ impl NormalizedAlgorithm {
27022760
},
27032761
_ => Err(Error::NotSupported),
27042762
},
2763+
NormalizedAlgorithm::EcKeyImportParams(algo) => ecdh_operation::import_key(
2764+
global,
2765+
algo,
2766+
format,
2767+
key_data,
2768+
extractable,
2769+
usages,
2770+
can_gc,
2771+
),
27052772
NormalizedAlgorithm::HmacImportParams(algo) => hmac_operation::import_key(
27062773
global,
27072774
algo,

0 commit comments

Comments
 (0)