crypto: Implement encrypt/decrypt for AES-CBC + JWK support (#33795)

* Add support for raw importKey with AES-CBC

Signed-off-by: Daniel Adams <msub2official@gmail.com>

* Support JWK import/export, importKey for AES-CBC

Signed-off-by: Daniel Adams <msub2official@gmail.com>

* Implement encrypt/decrypt for AES-CBC

Signed-off-by: Daniel Adams <msub2official@gmail.com>

* Update expectations

Signed-off-by: Daniel Adams <msub2official@gmail.com>

* Update Cargo.lock

Signed-off-by: Daniel Adams <msub2official@gmail.com>

* Pass MutableHandleObject as arg instead of returning raw pointer

Signed-off-by: Daniel Adams <msub2official@gmail.com>

* Swap order of checks in generate_key_aes_cbc

- Fixes WPT tests that expect to error on algorithm first before usages

Signed-off-by: Daniel Adams <msub2official@gmail.com>

* Avoid potential GC hazard with array_buffer_ptr

Signed-off-by: Daniel Adams <msub2official@gmail.com>

* Update expectations for discards context

Signed-off-by: Daniel Adams <msub2official@gmail.com>

---------

Signed-off-by: Daniel Adams <msub2official@gmail.com>
This commit is contained in:
Daniel Adams 2024-10-20 21:32:19 -10:00 committed by GitHub
parent 397c5adf79
commit 45267c9f28
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
55 changed files with 163938 additions and 2799 deletions

View file

@ -39,6 +39,8 @@ bincode = { workspace = true }
bitflags = { workspace = true }
bluetooth_traits = { workspace = true }
canvas_traits = { workspace = true }
cbc = { workspace = true }
cipher = { workspace = true }
chrono = { workspace = true }
content-security-policy = { workspace = true }
cookie = { workspace = true }

View file

@ -281,6 +281,10 @@ DOMInterfaces = {
'weakReferenceable': True,
},
'SubtleCrypto': {
'inRealms': ['Encrypt', 'Decrypt', 'GenerateKey', 'ImportKey', 'ExportKey']
},
#FIXME(jdm): This should be 'register': False, but then we don't generate enum types
'TestBinding': {
'inRealms': ['PromiseAttribute', 'PromiseNativeHandler'],
@ -323,31 +327,10 @@ DOMInterfaces = {
'canGc': ['Abort', 'GetResponseXML', 'Response'],
},
'XRSession': {
'inRealms': ['RequestReferenceSpace', 'UpdateRenderState', 'UpdateTargetFrameRate'],
'canGc': ['End', 'RequestReferenceSpace'],
},
'XRSystem': {
'inRealms': ['RequestSession', 'SupportsSessionMode'],
},
'XRBoundedReferenceSpace': {
'canGc': ['BoundsGeometry'],
},
'XRRay': {
'canGc': ['Origin', 'Direction'],
},
'XRRigidTransform': {
'canGc': ['Position', 'Orientation', 'Inverse'],
},
'XRReferenceSpace': {
'canGc': ['GetOffsetReferenceSpace'],
},
'XRFrame': {
'canGc': ['GetViewerPose', 'GetPose', 'GetJointPose'],
},
@ -356,8 +339,25 @@ DOMInterfaces = {
'canGc': ['GetPose'],
},
'SubtleCrypto': {
'inRealms': ['GenerateKey', 'ExportKey']
}
'XRRay': {
'canGc': ['Origin', 'Direction'],
},
'XRReferenceSpace': {
'canGc': ['GetOffsetReferenceSpace'],
},
'XRRigidTransform': {
'canGc': ['Position', 'Orientation', 'Inverse'],
},
'XRSession': {
'inRealms': ['RequestReferenceSpace', 'UpdateRenderState', 'UpdateTargetFrameRate'],
'canGc': ['End', 'RequestReferenceSpace'],
},
'XRSystem': {
'inRealms': ['RequestSession', 'SupportsSessionMode'],
},
}

View file

@ -85,6 +85,8 @@ pub enum Error {
InvalidModification,
/// NotReadableError DOMException
NotReadable,
/// DataError DOMException
Data,
/// OperationError DOMException
Operation,
@ -139,6 +141,7 @@ pub fn throw_dom_exception(cx: SafeJSContext, global: &GlobalScope, result: Erro
Error::TypeMismatch => DOMErrorName::TypeMismatchError,
Error::InvalidModification => DOMErrorName::InvalidModificationError,
Error::NotReadable => DOMErrorName::NotReadableError,
Error::Data => DOMErrorName::DataError,
Error::Operation => DOMErrorName::OperationError,
Error::Type(message) => unsafe {
assert!(!JS_IsExceptionPending(*cx));

View file

@ -85,6 +85,10 @@ impl CryptoKey {
self.algorithm.borrow().to_string()
}
pub fn usages(&self) -> &[KeyUsage] {
&self.usages
}
pub fn handle(&self) -> &Handle {
&self.handle
}

View file

@ -45,6 +45,7 @@ pub enum DOMErrorName {
DataCloneError = DOMExceptionConstants::DATA_CLONE_ERR,
EncodingError,
NotReadableError,
DataError,
OperationError,
}
@ -75,6 +76,7 @@ impl DOMErrorName {
"DataCloneError" => Some(DOMErrorName::DataCloneError),
"EncodingError" => Some(DOMErrorName::EncodingError),
"NotReadableError" => Some(DOMErrorName::NotReadableError),
"DataError" => Some(DOMErrorName::DataError),
"OperationError" => Some(DOMErrorName::OperationError),
_ => None,
}
@ -123,6 +125,7 @@ impl DOMException {
"The encoding operation (either encoded or decoding) failed."
},
DOMErrorName::NotReadableError => "The I/O read operation failed.",
DOMErrorName::DataError => "Provided data is inadequate.",
DOMErrorName::OperationError => {
"The operation failed for an operation-specific reason."
},

View file

@ -5,10 +5,16 @@
use std::ptr;
use std::rc::Rc;
use aes::cipher::block_padding::Pkcs7;
use aes::cipher::generic_array::GenericArray;
use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit};
use aes::{Aes128, Aes192, Aes256};
use base64::prelude::*;
use dom_struct::dom_struct;
use js::conversions::ConversionResult;
use js::jsapi::JSObject;
use js::jsval::ObjectValue;
use js::rust::MutableHandleObject;
use js::typedarray::ArrayBufferU8;
use servo_rand::{RngCore, ServoRng};
@ -18,7 +24,11 @@ use crate::dom::bindings::codegen::Bindings::CryptoKeyBinding::{
CryptoKeyMethods, KeyType, KeyUsage,
};
use crate::dom::bindings::codegen::Bindings::SubtleCryptoBinding::{
AesKeyGenParams, Algorithm, AlgorithmIdentifier, KeyAlgorithm, KeyFormat, SubtleCryptoMethods,
AesCbcParams, AesKeyGenParams, Algorithm, AlgorithmIdentifier, JsonWebKey, KeyAlgorithm,
KeyFormat, SubtleCryptoMethods,
};
use crate::dom::bindings::codegen::UnionTypes::{
ArrayBufferViewOrArrayBuffer, ArrayBufferViewOrArrayBufferOrJsonWebKey,
};
use crate::dom::bindings::error::Error;
use crate::dom::bindings::inheritance::Castable;
@ -26,6 +36,7 @@ use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::cryptokey::{CryptoKey, Handle};
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
@ -80,6 +91,13 @@ const NAMED_CURVE_P521: &str = "P-521";
#[allow(dead_code)]
static SUPPORTED_CURVES: &[&str] = &[NAMED_CURVE_P256, NAMED_CURVE_P384, NAMED_CURVE_P521];
type Aes128CbcEnc = cbc::Encryptor<Aes128>;
type Aes128CbcDec = cbc::Decryptor<Aes128>;
type Aes192CbcEnc = cbc::Encryptor<Aes192>;
type Aes192CbcDec = cbc::Decryptor<Aes192>;
type Aes256CbcEnc = cbc::Encryptor<Aes256>;
type Aes256CbcDec = cbc::Decryptor<Aes256>;
#[dom_struct]
pub struct SubtleCrypto {
reflector_: Reflector,
@ -115,6 +133,118 @@ impl SubtleCrypto {
}
impl SubtleCryptoMethods for SubtleCrypto {
/// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-encrypt>
fn Encrypt(
&self,
cx: JSContext,
algorithm: AlgorithmIdentifier,
key: &CryptoKey,
data: ArrayBufferViewOrArrayBuffer,
comp: InRealm,
) -> Rc<Promise> {
let normalized_algorithm = normalize_algorithm(cx, algorithm, "encrypt");
let promise = Promise::new_in_current_realm(comp);
let data = match data {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
let (task_source, canceller) = self.task_source_with_canceller();
let this = Trusted::new(self);
let trusted_promise = TrustedPromise::new(promise.clone());
let trusted_key = Trusted::new(key);
let alg = normalized_algorithm.clone();
let key_alg = key.algorithm();
let valid_usage = key.usages().contains(&KeyUsage::Encrypt);
let _ = task_source.queue_with_canceller(
task!(encrypt: move || {
let subtle = this.root();
let promise = trusted_promise.root();
let key = trusted_key.root();
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
let text = match alg {
Ok(NormalizedAlgorithm::AesCbcParams(key_gen_params)) => {
if !valid_usage || key_gen_params.name != key_alg {
Err(Error::InvalidAccess)
} else {
match subtle.encrypt_aes_cbc(
key_gen_params, &key, &data, cx, array_buffer_ptr.handle_mut()
) {
Ok(_) => Ok(array_buffer_ptr.handle()),
Err(e) => Err(e),
}
}
},
_ => Err(Error::NotSupported),
};
match text {
Ok(text) => promise.resolve_native(&*text),
Err(e) => promise.reject_error(e),
}
}),
&canceller,
);
promise
}
/// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-decrypt>
fn Decrypt(
&self,
cx: JSContext,
algorithm: AlgorithmIdentifier,
key: &CryptoKey,
data: ArrayBufferViewOrArrayBuffer,
comp: InRealm,
) -> Rc<Promise> {
let normalized_algorithm = normalize_algorithm(cx, algorithm, "decrypt");
let promise = Promise::new_in_current_realm(comp);
let data = match data {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
let (task_source, canceller) = self.task_source_with_canceller();
let this = Trusted::new(self);
let trusted_promise = TrustedPromise::new(promise.clone());
let trusted_key = Trusted::new(key);
let alg = normalized_algorithm.clone();
let key_alg = key.algorithm();
let valid_usage = key.usages().contains(&KeyUsage::Decrypt);
let _ = task_source.queue_with_canceller(
task!(decrypt: move || {
let subtle = this.root();
let promise = trusted_promise.root();
let key = trusted_key.root();
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
let text = match alg {
Ok(NormalizedAlgorithm::AesCbcParams(key_gen_params)) => {
if !valid_usage || key_gen_params.name != key_alg {
Err(Error::InvalidAccess)
} else {
match subtle.decrypt_aes_cbc(
key_gen_params, &key, &data, cx, array_buffer_ptr.handle_mut()
) {
Ok(_) => Ok(array_buffer_ptr.handle()),
Err(e) => Err(e),
}
}
},
_ => Err(Error::NotSupported),
};
match text {
Ok(text) => promise.resolve_native(&*text),
Err(e) => promise.reject_error(e),
}
}),
&canceller,
);
promise
}
/// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-generateKey>
fn GenerateKey(
&self,
@ -156,8 +286,80 @@ impl SubtleCryptoMethods for SubtleCrypto {
promise
}
/// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-importKey>
fn ImportKey(
&self,
cx: JSContext,
format: KeyFormat,
key_data: ArrayBufferViewOrArrayBufferOrJsonWebKey,
algorithm: AlgorithmIdentifier,
extractable: bool,
key_usages: Vec<KeyUsage>,
comp: InRealm,
) -> Rc<Promise> {
let normalized_algorithm = normalize_algorithm(cx, algorithm, "importKey");
let promise = Promise::new_in_current_realm(comp);
if let Err(e) = normalized_algorithm {
promise.reject_error(e);
return promise;
}
// TODO: Figure out a way to Send this data so per-algorithm JWK checks can happen
let data = match key_data {
ArrayBufferViewOrArrayBufferOrJsonWebKey::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBufferOrJsonWebKey::JsonWebKey(json_web_key) => {
if let Some(mut data_string) = json_web_key.k {
while data_string.len() % 4 != 0 {
data_string.push_str("=");
}
match BASE64_STANDARD.decode(data_string.to_string()) {
Ok(data) => data,
Err(_) => {
promise.reject_error(Error::Syntax);
return promise;
},
}
} else {
promise.reject_error(Error::Syntax);
return promise;
}
},
ArrayBufferViewOrArrayBufferOrJsonWebKey::ArrayBuffer(array_buffer) => {
array_buffer.to_vec()
},
};
let (task_source, canceller) = self.task_source_with_canceller();
let this = Trusted::new(self);
let trusted_promise = TrustedPromise::new(promise.clone());
let _ = task_source.queue_with_canceller(
task!(import_key: move || {
let subtle = this.root();
let promise = trusted_promise.root();
let alg = match normalized_algorithm {
Ok(NormalizedAlgorithm::Algorithm(name)) => name,
_ => {
promise.reject_error(Error::NotSupported);
return;
},
};
let imported_key = match alg.name.as_str() {
ALG_AES_CBC => subtle.import_key_aes_cbc(format, &data, extractable, key_usages),
_ => Err(Error::NotSupported),
};
match imported_key {
Ok(k) => promise.resolve_native(&k),
Err(e) => promise.reject_error(e),
};
}),
&canceller,
);
promise
}
/// <https://w3c.github.io/webcrypto/#SubtleCrypto-method-exportKey>
#[allow(unsafe_code)]
fn ExportKey(&self, format: KeyFormat, key: &CryptoKey, comp: InRealm) -> Rc<Promise> {
let promise = Promise::new_in_current_realm(comp);
@ -187,11 +389,18 @@ impl SubtleCryptoMethods for SubtleCrypto {
};
match exported_key {
Ok(k) => {
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
create_buffer_source::<ArrayBufferU8>(cx, &k, array_buffer_ptr.handle_mut())
.expect("failed to create buffer source for exported key.");
promise.resolve_native(&array_buffer_ptr.get())
match k {
AesExportedKey::Raw(k) => {
let cx = GlobalScope::get_cx();
rooted!(in(*cx) let mut array_buffer_ptr = ptr::null_mut::<JSObject>());
create_buffer_source::<ArrayBufferU8>(cx, &k, array_buffer_ptr.handle_mut())
.expect("failed to create buffer source for exported key.");
promise.resolve_native(&array_buffer_ptr.get())
},
AesExportedKey::Jwk(k) => {
promise.resolve_native(&k)
},
}
},
Err(e) => promise.reject_error(e),
}
@ -207,6 +416,7 @@ impl SubtleCryptoMethods for SubtleCrypto {
pub enum NormalizedAlgorithm {
#[allow(dead_code)]
Algorithm(SubtleAlgorithm),
AesCbcParams(SubtleAesCbcParams),
AesKeyGenParams(SubtleAesKeyGenParams),
}
@ -227,6 +437,26 @@ impl From<DOMString> for SubtleAlgorithm {
}
}
#[derive(Clone)]
pub struct SubtleAesCbcParams {
#[allow(dead_code)]
pub name: String,
pub iv: Vec<u8>,
}
impl From<RootedTraceableBox<AesCbcParams>> for SubtleAesCbcParams {
fn from(params: RootedTraceableBox<AesCbcParams>) -> Self {
let iv = match &params.iv {
ArrayBufferViewOrArrayBuffer::ArrayBufferView(view) => view.to_vec(),
ArrayBufferViewOrArrayBuffer::ArrayBuffer(buffer) => buffer.to_vec(),
};
SubtleAesCbcParams {
name: params.parent.name.to_string(),
iv,
}
}
}
#[derive(Clone)]
pub struct SubtleAesKeyGenParams {
#[allow(dead_code)]
@ -259,6 +489,14 @@ fn normalize_algorithm(
return Err(Error::Syntax);
};
match (algorithm.name.str().to_uppercase().as_str(), operation) {
(ALG_AES_CBC, "encrypt") | (ALG_AES_CBC, "decrypt") => {
let params_result =
AesCbcParams::new(cx, value.handle()).map_err(|_| Error::Operation)?;
let ConversionResult::Success(params) = params_result else {
return Err(Error::Syntax);
};
Ok(NormalizedAlgorithm::AesCbcParams(params.into()))
},
(ALG_AES_CBC, "generateKey") => {
let params_result =
AesKeyGenParams::new(cx, value.handle()).map_err(|_| Error::Operation)?;
@ -267,13 +505,96 @@ fn normalize_algorithm(
};
Ok(NormalizedAlgorithm::AesKeyGenParams(params.into()))
},
_ => Err(Error::NotSupported),
(ALG_AES_CBC, "importKey") => Ok(NormalizedAlgorithm::Algorithm(SubtleAlgorithm {
name: ALG_AES_CBC.to_string(),
})),
_ => return Err(Error::NotSupported),
}
},
}
}
impl SubtleCrypto {
/// <https://w3c.github.io/webcrypto/#aes-cbc-operations>
fn encrypt_aes_cbc(
&self,
params: SubtleAesCbcParams,
key: &CryptoKey,
data: &[u8],
cx: JSContext,
handle: MutableHandleObject,
) -> Result<(), Error> {
if params.iv.len() != 16 {
return Err(Error::Operation);
}
let mut plaintext = Vec::from(data);
let iv = GenericArray::from_slice(&params.iv);
let ct = match key.handle() {
Handle::Aes128(data) => {
let key_data = GenericArray::from_slice(&data);
Aes128CbcEnc::new(key_data, iv).encrypt_padded_vec_mut::<Pkcs7>(&mut plaintext)
},
Handle::Aes192(data) => {
let key_data = GenericArray::from_slice(&data);
Aes192CbcEnc::new(key_data, iv).encrypt_padded_vec_mut::<Pkcs7>(&mut plaintext)
},
Handle::Aes256(data) => {
let key_data = GenericArray::from_slice(&data);
Aes256CbcEnc::new(key_data, iv).encrypt_padded_vec_mut::<Pkcs7>(&mut plaintext)
},
};
create_buffer_source::<ArrayBufferU8>(cx, &ct, handle)
.expect("failed to create buffer source for exported key.");
Ok(())
}
/// <https://w3c.github.io/webcrypto/#aes-cbc-operations>
fn decrypt_aes_cbc(
&self,
params: SubtleAesCbcParams,
key: &CryptoKey,
data: &[u8],
cx: JSContext,
handle: MutableHandleObject,
) -> Result<(), Error> {
if params.iv.len() != 16 {
return Err(Error::Operation);
}
let mut ciphertext = Vec::from(data);
let iv = GenericArray::from_slice(&params.iv);
let plaintext = match key.handle() {
Handle::Aes128(data) => {
let key_data = GenericArray::from_slice(&data);
Aes128CbcDec::new(key_data, iv)
.decrypt_padded_mut::<Pkcs7>(ciphertext.as_mut_slice())
.map_err(|_| Error::Operation)?
},
Handle::Aes192(data) => {
let key_data = GenericArray::from_slice(&data);
Aes192CbcDec::new(key_data, iv)
.decrypt_padded_mut::<Pkcs7>(ciphertext.as_mut_slice())
.map_err(|_| Error::Operation)?
},
Handle::Aes256(data) => {
let key_data = GenericArray::from_slice(&data);
Aes256CbcDec::new(key_data, iv)
.decrypt_padded_mut::<Pkcs7>(ciphertext.as_mut_slice())
.map_err(|_| Error::Operation)?
},
};
create_buffer_source::<ArrayBufferU8>(cx, plaintext, handle)
.expect("failed to create buffer source for exported key.");
Ok(())
}
/// <https://w3c.github.io/webcrypto/#aes-cbc-operations>
fn generate_key_aes_cbc(
&self,
@ -281,9 +602,14 @@ impl SubtleCrypto {
key_gen_params: SubtleAesKeyGenParams,
extractable: bool,
) -> Result<DomRoot<CryptoKey>, Error> {
if !matches!(key_gen_params.length, 128 | 192 | 256) {
return Err(Error::Operation);
}
let mut rand = vec![0; key_gen_params.length as usize];
self.rng.borrow_mut().fill_bytes(&mut rand);
let handle = match key_gen_params.length {
128 => Handle::Aes128(rand),
192 => Handle::Aes192(rand),
256 => Handle::Aes256(rand),
_ => return Err(Error::Operation),
};
if usages.iter().any(|usage| {
!matches!(
@ -295,15 +621,6 @@ impl SubtleCrypto {
return Err(Error::Syntax);
}
let mut rand = vec![0; key_gen_params.length as usize];
self.rng.borrow_mut().fill_bytes(&mut rand);
let handle = match key_gen_params.length {
128 => Handle::Aes128(rand),
192 => Handle::Aes192(rand),
256 => Handle::Aes256(rand),
_ => return Err(Error::Operation),
};
Ok(CryptoKey::new(
&self.global(),
KeyType::Secret,
@ -317,18 +634,95 @@ impl SubtleCrypto {
}
/// <https://w3c.github.io/webcrypto/#aes-cbc-operations>
fn export_key_aes_cbc(&self, format: KeyFormat, key: &CryptoKey) -> Result<Vec<u8>, Error> {
fn import_key_aes_cbc(
&self,
format: KeyFormat,
data: &[u8],
extractable: bool,
usages: Vec<KeyUsage>,
) -> Result<DomRoot<CryptoKey>, Error> {
if usages.iter().any(|usage| {
!matches!(
usage,
KeyUsage::Encrypt | KeyUsage::Decrypt | KeyUsage::WrapKey | KeyUsage::UnwrapKey
)
}) || usages.is_empty()
{
return Err(Error::Syntax);
}
if !matches!(format, KeyFormat::Raw | KeyFormat::Jwk) {
return Err(Error::NotSupported);
}
let handle = match data.len() * 8 {
128 => Handle::Aes128(data.to_vec()),
192 => Handle::Aes192(data.to_vec()),
256 => Handle::Aes256(data.to_vec()),
_ => return Err(Error::Data),
};
Ok(CryptoKey::new(
&self.global(),
KeyType::Secret,
extractable,
KeyAlgorithm {
name: DOMString::from(ALG_AES_CBC),
},
usages,
handle,
))
}
/// <https://w3c.github.io/webcrypto/#aes-cbc-operations>
fn export_key_aes_cbc(
&self,
format: KeyFormat,
key: &CryptoKey,
) -> Result<AesExportedKey, Error> {
match format {
KeyFormat::Raw => match key.handle() {
Handle::Aes128(key) => Ok(key.as_slice().to_vec()),
Handle::Aes192(key) => Ok(key.as_slice().to_vec()),
Handle::Aes256(key) => Ok(key.as_slice().to_vec()),
Handle::Aes128(key) => Ok(AesExportedKey::Raw(key.as_slice().to_vec())),
Handle::Aes192(key) => Ok(AesExportedKey::Raw(key.as_slice().to_vec())),
Handle::Aes256(key) => Ok(AesExportedKey::Raw(key.as_slice().to_vec())),
},
KeyFormat::Jwk => {
// TODO: Support jwk
Err(Error::NotSupported)
let (alg, k) = match key.handle() {
Handle::Aes128(key) => data_to_jwk_params("A128CBC", key.as_slice()),
Handle::Aes192(key) => data_to_jwk_params("A192CBC", key.as_slice()),
Handle::Aes256(key) => data_to_jwk_params("A256CBC", key.as_slice()),
};
let jwk = JsonWebKey {
alg: Some(alg),
crv: None,
d: None,
dp: None,
dq: None,
e: None,
ext: Some(key.Extractable()),
k: Some(k),
key_ops: None,
kty: Some(DOMString::from("oct")),
n: None,
oth: None,
p: None,
q: None,
qi: None,
use_: None,
x: None,
y: None,
};
Ok(AesExportedKey::Jwk(jwk))
},
_ => Err(Error::NotSupported),
}
}
}
pub enum AesExportedKey {
Raw(Vec<u8>),
Jwk(JsonWebKey),
}
fn data_to_jwk_params(alg: &str, key: &[u8]) -> (DOMString, DOMString) {
let mut data = BASE64_STANDARD.encode(key);
data.retain(|c| c != '=');
(DOMString::from(alg), DOMString::from(data))
}

View file

@ -20,12 +20,12 @@ enum KeyFormat { "raw", "spki", "pkcs8", "jwk" };
[SecureContext,Exposed=(Window,Worker),Pref="dom.crypto.subtle.enabled"]
interface SubtleCrypto {
// Promise<any> encrypt(AlgorithmIdentifier algorithm,
// CryptoKey key,
// BufferSource data);
// Promise<any> decrypt(AlgorithmIdentifier algorithm,
// CryptoKey key,
// BufferSource data);
Promise<any> encrypt(AlgorithmIdentifier algorithm,
CryptoKey key,
BufferSource data);
Promise<any> decrypt(AlgorithmIdentifier algorithm,
CryptoKey key,
BufferSource data);
// Promise<any> sign(AlgorithmIdentifier algorithm,
// CryptoKey key,
// BufferSource data);
@ -48,11 +48,11 @@ interface SubtleCrypto {
// CryptoKey baseKey,
// optional unsigned long? length = null);
// Promise<CryptoKey> importKey(KeyFormat format,
// (BufferSource or JsonWebKey) keyData,
// AlgorithmIdentifier algorithm,
// boolean extractable,
// sequence<KeyUsage> keyUsages );
Promise<CryptoKey> importKey(KeyFormat format,
(BufferSource or JsonWebKey) keyData,
AlgorithmIdentifier algorithm,
boolean extractable,
sequence<KeyUsage> keyUsages );
Promise<any> exportKey(KeyFormat format, CryptoKey key);
// Promise<any> wrapKey(KeyFormat format,
@ -85,3 +85,37 @@ dictionary AesDerivedKeyParams : Algorithm {
dictionary AesCbcParams : Algorithm {
required BufferSource iv;
};
// JWK
dictionary RsaOtherPrimesInfo {
// The following fields are defined in Section 6.3.2.7 of JSON Web Algorithms
DOMString r;
DOMString d;
DOMString t;
};
dictionary JsonWebKey {
// The following fields are defined in Section 3.1 of JSON Web Key
DOMString kty;
DOMString use;
sequence<DOMString> key_ops;
DOMString alg;
// The following fields are defined in JSON Web Key Parameters Registration
boolean ext;
// The following fields are defined in Section 6 of JSON Web Algorithms
DOMString crv;
DOMString x;
DOMString y;
DOMString d;
DOMString n;
DOMString e;
DOMString p;
DOMString q;
DOMString dp;
DOMString dq;
DOMString qi;
sequence<RsaOtherPrimesInfo> oth;
DOMString k;
};