Skip to main content

ferrijs_std/crypto/subtle/
crypto_key.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3use 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}