1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::num::NonZero;
use aws_lc_rs::pbkdf2;
use js::context::JSContext;
use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{KeyType, KeyUsage};
use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::KeyFormat;
use crate::dom::bindings::error::Error;
use crate::dom::bindings::root::DomRoot;
use crate::dom::cryptokey::{CryptoKey, Handle};
use crate::dom::globalscope::GlobalScope;
use crate::dom::subtlecrypto::{
CryptoAlgorithm, KeyAlgorithmAndDerivatives, NormalizedAlgorithm, SubtleKeyAlgorithm,
SubtlePbkdf2Params,
};
/// <https://w3c.github.io/webcrypto/#pbkdf2-operations-derive-bits>
pub(crate) fn derive_bits(
normalized_algorithm: &SubtlePbkdf2Params,
key: &CryptoKey,
length: Option<u32>,
) -> Result<Vec<u8>, Error> {
// Step 1. If length is null or is not a multiple of 8, then throw an OperationError.
let Some(length) = length else {
return Err(Error::Operation(Some("Length is null".into())));
};
if length % 8 != 0 {
return Err(Error::Operation(Some(
"Length is not a multiple of 8".into(),
)));
};
// Step 2. If the iterations member of normalizedAlgorithm is zero, then throw an OperationError.
let Ok(iterations) = NonZero::<u32>::try_from(normalized_algorithm.iterations) else {
return Err(Error::Operation(Some(
"Normalized algorithm's iterations is zero".into(),
)));
};
// Step 3. If length is zero, return an empty byte sequence.
if length == 0 {
return Ok(Vec::new());
}
// Step 4. Let prf be the MAC Generation function described in Section 4 of [FIPS-198-1] using
// the hash function described by the hash member of normalizedAlgorithm.
let prf = match normalized_algorithm.hash.name() {
CryptoAlgorithm::Sha1 => pbkdf2::PBKDF2_HMAC_SHA1,
CryptoAlgorithm::Sha256 => pbkdf2::PBKDF2_HMAC_SHA256,
CryptoAlgorithm::Sha384 => pbkdf2::PBKDF2_HMAC_SHA384,
CryptoAlgorithm::Sha512 => pbkdf2::PBKDF2_HMAC_SHA512,
_ => {
return Err(Error::NotSupported(Some(
"Normalized algorithm's hash name is not supported".into(),
)));
},
};
// Step 5. Let result be the result of performing the PBKDF2 operation defined in Section 5.2
// of [RFC8018] using prf as the pseudo-random function, PRF, the password represented by the
// [[handle]] internal slot of key as the password, P, the salt attribute of
// normalizedAlgorithm as the salt, S, the value of the iterations attribute of
// normalizedAlgorithm as the iteration count, c, and length divided by 8 as the intended key
// length, dkLen.
let mut result = vec![0; length as usize / 8];
pbkdf2::derive(
prf,
iterations,
&normalized_algorithm.salt,
key.handle().as_bytes(),
&mut result,
);
// Step 5. If the key derivation operation fails, then throw an OperationError.
// TODO: Investigate when key derivation can fail and how ring handles that case
// (pbkdf2::derive does not return a Result type)
// Step 6. Return result
Ok(result)
}
/// <https://w3c.github.io/webcrypto/#pbkdf2-operations-import-key>
pub(crate) fn import_key(
cx: &mut JSContext,
global: &GlobalScope,
format: KeyFormat,
key_data: &[u8],
extractable: bool,
usages: Vec<KeyUsage>,
) -> Result<DomRoot<CryptoKey>, Error> {
// Step 1. If format is not "raw", throw a NotSupportedError
if !matches!(format, KeyFormat::Raw | KeyFormat::Raw_secret) {
return Err(Error::NotSupported(Some("Format is not raw".into())));
}
// Step 2. If usages contains a value that is not "deriveKey" or "deriveBits", then throw a SyntaxError.
if usages
.iter()
.any(|usage| !matches!(usage, KeyUsage::DeriveKey | KeyUsage::DeriveBits)) ||
usages.is_empty()
{
return Err(Error::Syntax(Some(
"Usages is empty or contains a value that is not a 'deriveKey' or 'deriveBits'".into(),
)));
}
// Step 3. If extractable is not false, then throw a SyntaxError.
if extractable {
return Err(Error::Syntax(Some("Extractable is not false".into())));
}
// Step 4. Let key be a new CryptoKey representing keyData.
// Step 5. Set the [[type]] internal slot of key to "secret".
// Step 6. Let algorithm be a new KeyAlgorithm object.
// Step 7. Set the name attribute of algorithm to "PBKDF2".
// Step 8. Set the [[algorithm]] internal slot of key to algorithm.
let algorithm = SubtleKeyAlgorithm {
name: CryptoAlgorithm::Pbkdf2,
};
let key = CryptoKey::new(
cx,
global,
KeyType::Secret,
extractable,
KeyAlgorithmAndDerivatives::KeyAlgorithm(algorithm),
usages,
Handle::Pbkdf2(key_data.to_vec().into()),
);
// Step 9. Return key.
Ok(key)
}
/// <https://w3c.github.io/webcrypto/#pbkdf2-operations-get-key-length>
pub(crate) fn get_key_length() -> Result<Option<u32>, Error> {
// Step 1. Return null.
Ok(None)
}