script/dom/subtlecrypto/hkdf_operation.rs
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use aws_lc_rs::hkdf;
6
7use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{KeyType, KeyUsage};
8use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::KeyFormat;
9use crate::dom::bindings::error::Error;
10use crate::dom::bindings::root::DomRoot;
11use crate::dom::cryptokey::{CryptoKey, Handle};
12use crate::dom::globalscope::GlobalScope;
13use crate::dom::subtlecrypto::{
14 ALG_HKDF, ALG_SHA1, ALG_SHA256, ALG_SHA384, ALG_SHA512, KeyAlgorithmAndDerivatives,
15 SubtleHkdfParams, SubtleKeyAlgorithm,
16};
17use crate::script_runtime::CanGc;
18
19/// <https://w3c.github.io/webcrypto/#hkdf-operations-derive-bits>
20pub(crate) fn derive_bits(
21 normalized_algorithm: &SubtleHkdfParams,
22 key: &CryptoKey,
23 length: Option<u32>,
24) -> Result<Vec<u8>, Error> {
25 // Step 1. If length is null or is not a multiple of 8, then throw an OperationError.
26 let Some(length) = length else {
27 return Err(Error::Operation);
28 };
29 if length % 8 != 0 {
30 return Err(Error::Operation);
31 };
32
33 // Step 2. Let keyDerivationKey be the secret represented by the [[handle]] internal slot of key.
34 let key_derivation_key = key.handle().as_bytes();
35
36 // NOTE: Since https://github.com/w3c/webcrypto/pull/380, WebCrypto allows zero length in HKDF
37 // deriveBits operation. However, in Step 4 below, `output_key_material.fill(&mut result)`
38 // gives an error when length is 0. Therefore, when length is zero, we immediately returns an
39 // empty byte sequence beforehand.
40 if length == 0 {
41 return Ok(Vec::new());
42 }
43
44 // Step 3. Let result be the result of performing the HKDF extract and then the HKDF expand
45 // step described in Section 2 of [RFC5869] using:
46 // * the hash member of normalizedAlgorithm as Hash,
47 // * keyDerivationKey as the input keying material, IKM,
48 // * the salt member of normalizedAlgorithm as salt,
49 // * the info member of normalizedAlgorithm as info,
50 // * length divided by 8 as the value of L,
51 // Step 4. If the key derivation operation fails, then throw an OperationError.
52 let mut result = vec![0; length as usize / 8];
53 let algorithm = match normalized_algorithm.hash.name.as_str() {
54 ALG_SHA1 => hkdf::HKDF_SHA1_FOR_LEGACY_USE_ONLY,
55 ALG_SHA256 => hkdf::HKDF_SHA256,
56 ALG_SHA384 => hkdf::HKDF_SHA384,
57 ALG_SHA512 => hkdf::HKDF_SHA512,
58 _ => {
59 return Err(Error::NotSupported);
60 },
61 };
62 let salt = hkdf::Salt::new(algorithm, &normalized_algorithm.salt);
63 let info = normalized_algorithm.info.as_slice();
64 let pseudo_random_key = salt.extract(key_derivation_key);
65 let Ok(output_key_material) = pseudo_random_key.expand(std::slice::from_ref(&info), algorithm)
66 else {
67 return Err(Error::Operation);
68 };
69 if output_key_material.fill(&mut result).is_err() {
70 return Err(Error::Operation);
71 };
72
73 // Step 5. Return result.
74 Ok(result)
75}
76
77/// <https://w3c.github.io/webcrypto/#hkdf-operations-import-key>
78pub(crate) fn import_key(
79 global: &GlobalScope,
80 format: KeyFormat,
81 key_data: &[u8],
82 extractable: bool,
83 usages: Vec<KeyUsage>,
84 can_gc: CanGc,
85) -> Result<DomRoot<CryptoKey>, Error> {
86 // Step 1. Let keyData be the key data to be imported.
87
88 // Step 2. If format is "raw":
89 if format == KeyFormat::Raw {
90 // Step 2.1. If usages contains a value that is not "deriveKey" or "deriveBits", then throw
91 // a SyntaxError.
92 if usages
93 .iter()
94 .any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits)) ||
95 usages.is_empty()
96 {
97 return Err(Error::Syntax(None));
98 }
99
100 // Step 2.2. If extractable is not false, then throw a SyntaxError.
101 if extractable {
102 return Err(Error::Syntax(None));
103 }
104
105 // Step 2.3. Let key be a new CryptoKey representing the key data provided in keyData.
106 // Step 2.4. Set the [[type]] internal slot of key to "secret".
107 // Step 2.5. Let algorithm be a new KeyAlgorithm object.
108 // Step 2.6. Set the name attribute of algorithm to "HKDF".
109 // Step 2.7. Set the [[algorithm]] internal slot of key to algorithm.
110 let algorithm = SubtleKeyAlgorithm {
111 name: ALG_HKDF.to_string(),
112 };
113 let key = CryptoKey::new(
114 global,
115 KeyType::Secret,
116 extractable,
117 KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
118 usages,
119 Handle::Hkdf(key_data.to_vec()),
120 can_gc,
121 );
122
123 // Step 2.8. Return key.
124 Ok(key)
125 }
126 // Otherwise:
127 else {
128 // throw a NotSupportedError.
129 Err(Error::NotSupported)
130 }
131}
132
133/// <https://w3c.github.io/webcrypto/#hkdf-operations-get-key-length>
134pub(crate) fn get_key_length() -> Result<Option<u32>, Error> {
135 // Step 1. Return null.
136 Ok(None)
137}