Implement subtlecrypto.deriveKey (#34185)

* Implement NormalizedAlgorithm::get_key_length

This is a minimal implementation, which will make
the DeriveKey operation work for AES-CTR keys in
the future.

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Implement SubtleCrypto.deriveKey

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

* Update WPT expectations

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
This commit is contained in:
Simon Wülker 2024-11-08 13:59:01 +01:00 committed by GitHub
parent fe58556c0b
commit 6b94b2c684
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 159 additions and 13045 deletions

View file

@ -428,8 +428,8 @@ DOMInterfaces = {
},
'SubtleCrypto': {
'inRealms': ['Encrypt', 'Decrypt', 'GenerateKey', 'DeriveBits', 'Digest', 'ImportKey', 'ExportKey'],
'canGc': ['Encrypt', 'Decrypt', 'GenerateKey', 'DeriveBits', 'Digest', 'ImportKey', 'ExportKey'],
'inRealms': ['Encrypt', 'Decrypt', 'GenerateKey', 'DeriveKey', 'DeriveBits', 'Digest', 'ImportKey', 'ExportKey'],
'canGc': ['Encrypt', 'Decrypt', 'GenerateKey', 'DeriveKey', 'DeriveBits', 'Digest', 'ImportKey', 'ExportKey'],
},
'SVGElement': {

View file

@ -389,6 +389,137 @@ impl SubtleCryptoMethods for SubtleCrypto {
promise
}
/// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-deriveKey>
fn DeriveKey(
&self,
cx: SafeJSContext,
algorithm: AlgorithmIdentifier,
base_key: &CryptoKey,
derived_key_type: AlgorithmIdentifier,
extractable: bool,
key_usages: Vec<KeyUsage>,
comp: InRealm,
can_gc: CanGc,
) -> Rc<Promise> {
// Step 1. Let algorithm, baseKey, derivedKeyType, extractable and usages be the algorithm, baseKey,
// derivedKeyType, extractable and keyUsages parameters passed to the deriveKey() method, respectively.
// Step 2. Let normalizedAlgorithm be the result of normalizing an algorithm, with alg set to algorithm
// and op set to "deriveBits".
let promise = Promise::new_in_current_realm(comp, can_gc);
let normalized_algorithm = match normalize_algorithm(cx, &algorithm, "deriveBits") {
Ok(algorithm) => algorithm,
Err(e) => {
// Step 3. If an error occurred, return a Promise rejected with normalizedAlgorithm.
promise.reject_error(e);
return promise;
},
};
// Step 4. Let normalizedDerivedKeyAlgorithmImport be the result of normalizing an algorithm,
// with alg set to derivedKeyType and op set to "importKey".
let normalized_derived_key_algorithm_import =
match normalize_algorithm(cx, &derived_key_type, "importKey") {
Ok(algorithm) => algorithm,
Err(e) => {
// Step 5. If an error occurred, return a Promise rejected with normalizedDerivedKeyAlgorithmImport.
promise.reject_error(e);
return promise;
},
};
// Step 6. Let normalizedDerivedKeyAlgorithmLength be the result of normalizing an algorithm, with alg set
// to derivedKeyType and op set to "get key length".
let normalized_derived_key_algorithm_length =
match normalize_algorithm(cx, &derived_key_type, "get key length") {
Ok(algorithm) => algorithm,
Err(e) => {
// Step 7. If an error occurred, return a Promise rejected with normalizedDerivedKeyAlgorithmLength.
promise.reject_error(e);
return promise;
},
};
// Step 8. Let promise be a new Promise.
// NOTE: We created the promise earlier, after Step 1.
// Step 9. Return promise and perform the remaining steps in parallel.
let (task_source, canceller) = self.task_source_with_canceller();
let trusted_promise = TrustedPromise::new(promise.clone());
let trusted_base_key = Trusted::new(base_key);
let this = Trusted::new(self);
let _ = task_source.queue_with_canceller(
task!(derive_key: move || {
// Step 10. If the following steps or referenced procedures say to throw an error, reject promise
// with the returned error and then terminate the algorithm.
// TODO Step 11. If the name member of normalizedAlgorithm is not equal to the name attribute of the #
// [[algorithm]] internal slot of baseKey then throw an InvalidAccessError.
let promise = trusted_promise.root();
let base_key = trusted_base_key.root();
let subtle = this.root();
// Step 12. If the [[usages]] internal slot of baseKey does not contain an entry that is
// "deriveKey", then throw an InvalidAccessError.
if !base_key.usages().contains(&KeyUsage::DeriveKey) {
promise.reject_error(Error::InvalidAccess);
return;
}
// Step 13. Let length be the result of performing the get key length algorithm specified by
// normalizedDerivedKeyAlgorithmLength using derivedKeyType.
let length = match normalized_derived_key_algorithm_length.get_key_length() {
Ok(length) => length,
Err(e) => {
promise.reject_error(e);
return;
}
};
// Step 14. Let secret be the result of performing the derive bits operation specified by
// normalizedAlgorithm using key, algorithm and length.
let secret = match normalized_algorithm.derive_bits(&base_key, Some(length as u32)){
Ok(secret) => secret,
Err(e) => {
promise.reject_error(e);
return;
}
};
// Step 15. Let result be the result of performing the import key operation specified by
// normalizedDerivedKeyAlgorithmImport using "raw" as format, secret as keyData, derivedKeyType as
// algorithm and using extractable and usages.
let result = normalized_derived_key_algorithm_import.import_key(
&subtle,
KeyFormat::Raw,
&secret,
extractable,
key_usages
);
let result = match result {
Ok(key) => key,
Err(e) => {
promise.reject_error(e);
return;
}
};
// Step 17. If the [[type]] internal slot of result is "secret" or "private" and usages
// is empty, then throw a SyntaxError.
if matches!(result.Type(), KeyType::Secret | KeyType::Private) && result.usages().is_empty() {
promise.reject_error(Error::Syntax);
return;
}
// Step 17. Resolve promise with result.
promise.resolve_native(&*result);
}),
&canceller,
);
promise
}
/// <https://w3c.github.io/webcrypto/#dfn-SubtleCrypto-method-deriveBits>
fn DeriveBits(
&self,
@ -1235,6 +1366,15 @@ impl NormalizedAlgorithm {
}
}
fn get_key_length(&self) -> Result<u16, Error> {
match self {
Self::AesCtrParams(aes_ctr_params) => {
get_key_length_for_aes(aes_ctr_params.length as u16)
},
_ => Err(Error::NotSupported),
}
}
fn import_key(
&self,
subtle: &SubtleCrypto,
@ -1275,3 +1415,15 @@ impl NormalizedAlgorithm {
Ok(digest::digest(algorithm, data))
}
}
/// <https://w3c.github.io/webcrypto/#aes-ctr-operations>
fn get_key_length_for_aes(length: u16) -> Result<u16, Error> {
// Step 1. If the length member of normalizedDerivedKeyAlgorithm is not 128, 192 or 256,
// then throw an OperationError.
if !matches!(length, 128 | 192 | 256) {
return Err(Error::Operation);
}
// Step 2. Return the length member of normalizedDerivedKeyAlgorithm.
Ok(length)
}

View file

@ -39,11 +39,11 @@ interface SubtleCrypto {
Promise<any> generateKey(AlgorithmIdentifier algorithm,
boolean extractable,
sequence<KeyUsage> keyUsages );
// Promise<any> deriveKey(AlgorithmIdentifier algorithm,
// CryptoKey baseKey,
// AlgorithmIdentifier derivedKeyType,
// boolean extractable,
// sequence<KeyUsage> keyUsages );
Promise<any> deriveKey(AlgorithmIdentifier algorithm,
CryptoKey baseKey,
AlgorithmIdentifier derivedKeyType,
boolean extractable,
sequence<KeyUsage> keyUsages );
Promise<ArrayBuffer> deriveBits(AlgorithmIdentifier algorithm,
CryptoKey baseKey,
optional unsigned long? length = null);