ferrijs_std/crypto/subtle/
crypto_key.rs1use std::rc::Rc;
4
5use crate::str_enum;
6use crate::utils::{clone::StructuredClone};
7use rquickjs::{
8 atom::PredefinedAtom,
9 class::{Trace, Tracer},
10 Class, Ctx, Exception, IntoJs, Object, Result, Value,
11};
12
13use crate::crypto::provider::CryptoError;
14
15use super::key_algorithm::KeyAlgorithm;
16
17#[derive(PartialEq, Clone, Copy)]
18pub enum KeyKind {
19 Secret,
20 Private,
21 Public,
22}
23
24str_enum!(KeyKind,Secret => "secret", Private => "private", Public => "public");
25
26#[rquickjs::class]
27#[derive(rquickjs::JsLifetime)]
28pub struct CryptoKey<'js> {
29 pub kind: KeyKind,
30 pub extractable: bool,
31 pub algorithm: KeyAlgorithm,
32 pub name: Box<str>,
33 pub usages: Vec<String>,
34 pub handle: Rc<[u8]>,
35 algorithm_cache: Option<Object<'js>>,
36 usages_cache: Option<Value<'js>>,
37}
38
39impl<'js> CryptoKey<'js> {
40 pub fn new<N, H>(
41 kind: KeyKind,
42 name: N,
43 extractable: bool,
44 algorithm: KeyAlgorithm,
45 usages: Vec<String>,
46 handle: H,
47 ) -> Self
48 where
49 N: Into<Box<str>>,
50 H: Into<Rc<[u8]>>,
51 {
52 Self {
53 kind,
54 extractable,
55 algorithm,
56 name: name.into(),
57 usages,
58 handle: handle.into(),
59 algorithm_cache: None,
60 usages_cache: None,
61 }
62 }
63}
64
65impl<'js> Trace<'js> for CryptoKey<'js> {
66 fn trace<'a>(&self, tracer: Tracer<'a, 'js>) {
67 if let Some(cached) = &self.algorithm_cache {
68 cached.trace(tracer);
69 }
70 if let Some(cached) = &self.usages_cache {
71 cached.trace(tracer);
72 }
73 }
74}
75
76impl<'js> StructuredClone<'js> for CryptoKey<'js> {
77 fn structured_clone(&self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
78 Ok(Class::instance(
79 ctx.clone(),
80 CryptoKey {
81 kind: self.kind,
82 extractable: self.extractable,
83 algorithm: self.algorithm.clone(),
84 name: self.name.clone(),
85 usages: self.usages.clone(),
86 handle: self.handle.clone(),
87 algorithm_cache: None,
88 usages_cache: None,
89 },
90 )?
91 .into_value())
92 }
93}
94
95#[rquickjs::methods]
96impl<'js> CryptoKey<'js> {
97 #[qjs(constructor)]
98 fn constructor(ctx: Ctx<'_>) -> Result<Self> {
99 Err(Exception::throw_type(&ctx, "Illegal constructor"))
100 }
101
102 #[qjs(get, rename = "type")]
103 pub fn get_type(&self) -> &str {
104 self.kind.as_str()
105 }
106
107 #[qjs(get)]
108 pub fn extractable(&self) -> bool {
109 self.extractable
110 }
111
112 #[qjs(prop, rename = PredefinedAtom::SymbolToStringTag, configurable)]
113 pub fn to_string_tag() -> &'static str {
114 stringify!(CryptoKey)
115 }
116
117 #[qjs(get)]
118 pub fn algorithm(&mut self, ctx: Ctx<'js>) -> Result<Value<'js>> {
119 if let Some(cached) = &self.algorithm_cache {
120 return Ok(cached.clone().into_value());
121 }
122 let obj = self.algorithm.as_object(&ctx, self.name.as_ref())?;
123 self.algorithm_cache = Some(obj.clone());
124 Ok(obj.into_value())
125 }
126
127 #[qjs(get)]
128 pub fn usages(&mut self, ctx: Ctx<'js>) -> Result<Value<'js>> {
129 if let Some(cached) = &self.usages_cache {
130 return Ok(cached.clone());
131 }
132 let arr = self.usages.clone().into_js(&ctx)?;
133 self.usages_cache = Some(arr.clone());
134 Ok(arr)
135 }
136}
137
138impl<'js> CryptoKey<'js> {
139 pub fn check_validity(&self, usage: &str) -> std::result::Result<(), CryptoError> {
140 for key in self.usages.iter() {
141 if key == usage {
142 return Ok(());
143 }
144 }
145 Err(CryptoError::InvalidAccess(Some(
146 [
147 "CryptoKey with '",
148 self.name.as_ref(),
149 "', doesn't support '",
150 usage,
151 "'",
152 ]
153 .concat()
154 .into(),
155 )))
156 }
157
158 pub fn check_kind(&self, expected: KeyKind) -> std::result::Result<(), CryptoError> {
159 if self.kind != expected {
160 return Err(CryptoError::InvalidAccess(Some("Invalid key type".into())));
161 }
162
163 Ok(())
164 }
165}