Skip to main content

aranya_daemon_api/crypto/
keys.rs

1use core::{borrow::Borrow, fmt, marker::PhantomData};
2use std::iter;
3
4use anyhow::Result;
5use aranya_crypto::{
6    dangerous::spideroak_crypto::{
7        import::ImportError,
8        kem::{DecapKey as _, Kem},
9        keys::PublicKey,
10        signer::PkError,
11    },
12    id::{IdError, IdExt, Identified},
13    unwrapped, CipherSuite, Engine, Oids, Random,
14};
15use aranya_id::custom_id;
16use ciborium as cbor;
17use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
18
19custom_id! {
20    /// Uniquely identifies an [`ApiKey`].
21    pub struct ApiKeyId;
22}
23
24/// The daemon's secret key used to encrypt data sent over the
25/// API.
26pub struct ApiKey<CS: CipherSuite>(<<CS as CipherSuite>::Kem as Kem>::DecapKey);
27
28impl<CS: CipherSuite> ApiKey<CS> {
29    pub(crate) fn new<CE>(eng: &CE) -> Self
30    where
31        CE: Engine<CS = CS>,
32    {
33        Self(Random::random(eng))
34    }
35
36    /// Returns the key's unique ID.
37    #[inline]
38    pub fn id(&self) -> Result<ApiKeyId, IdError> {
39        self.public()?.id()
40    }
41
42    /// Returns the public half of the key.
43    pub fn public(&self) -> Result<PublicApiKey<CS>, PkError> {
44        Ok(PublicApiKey(self.0.public()?))
45    }
46
47    pub(crate) fn as_inner(&self) -> &<<CS as CipherSuite>::Kem as Kem>::DecapKey {
48        &self.0
49    }
50
51    /// Generates a random API key.
52    pub fn generate<CE>(eng: &CE) -> Self
53    where
54        CE: Engine<CS = CS>,
55    {
56        Self::new(eng)
57    }
58}
59
60impl<CS: CipherSuite> Clone for ApiKey<CS> {
61    #[inline]
62    fn clone(&self) -> Self {
63        Self(self.0.clone())
64    }
65}
66
67impl<CS: CipherSuite> fmt::Display for ApiKey<CS> {
68    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69        write!(f, "{}", self.id().map_err(|_| fmt::Error)?)
70    }
71}
72
73impl<CS: CipherSuite> fmt::Debug for ApiKey<CS> {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        f.debug_tuple("ApiKey")
76            .field(&self.id().map_err(|_| fmt::Error)?)
77            .finish()
78    }
79}
80
81impl<CS: CipherSuite> Identified for ApiKey<CS> {
82    type Id = ApiKeyId;
83
84    #[inline]
85    fn id(&self) -> Result<Self::Id, IdError> {
86        self.id()
87    }
88}
89
90unwrapped! {
91    name: ApiKey;
92    type: Decap;
93    into: |key: Self| { key.0 };
94    from: |key| { Self(key) };
95}
96
97/// The public half of [`ApiKey`].
98pub struct PublicApiKey<CS: CipherSuite>(<<CS as CipherSuite>::Kem as Kem>::EncapKey);
99
100impl<CS: CipherSuite> PublicApiKey<CS> {
101    /// Returns the key's unique ID.
102    #[inline]
103    pub fn id(&self) -> Result<ApiKeyId, IdError> {
104        let pk = &self.0.export();
105        let id = ApiKeyId::new::<CS>(b"ApiKey", iter::once(pk.borrow()));
106        Ok(id)
107    }
108
109    pub(crate) fn as_inner(&self) -> &<<CS as CipherSuite>::Kem as Kem>::EncapKey {
110        &self.0
111    }
112
113    /// Encodes the public key as bytes.
114    pub fn encode(&self) -> Result<Vec<u8>> {
115        let mut buf = Vec::new();
116        cbor::into_writer(self, &mut buf)?;
117        Ok(buf)
118    }
119
120    /// Decodes the public key from bytes.
121    pub fn decode(data: &[u8]) -> Result<Self> {
122        Ok(cbor::from_reader(data)?)
123    }
124}
125
126impl<CS: CipherSuite> Clone for PublicApiKey<CS> {
127    #[inline]
128    fn clone(&self) -> Self {
129        Self(self.0.clone())
130    }
131}
132
133impl<CS: CipherSuite> AsRef<PublicApiKey<CS>> for PublicApiKey<CS> {
134    #[inline]
135    fn as_ref(&self) -> &Self {
136        self
137    }
138}
139
140impl<CS: CipherSuite> Eq for PublicApiKey<CS> {}
141impl<CS: CipherSuite> PartialEq for PublicApiKey<CS> {
142    #[inline]
143    fn eq(&self, other: &Self) -> bool {
144        match (self.id(), other.id()) {
145            (Ok(lhs), Ok(rhs)) => lhs == rhs,
146            _ => false,
147        }
148    }
149}
150
151impl<CS: CipherSuite> fmt::Display for PublicApiKey<CS> {
152    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153        write!(f, "{}", self.id().map_err(|_| fmt::Error)?)
154    }
155}
156
157impl<CS: CipherSuite> fmt::Debug for PublicApiKey<CS> {
158    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159        write!(
160            f,
161            concat!(stringify!(PublicApiKey), " {}"),
162            self.id().map_err(|_| fmt::Error)?
163        )
164    }
165}
166
167impl<CS: CipherSuite> Serialize for PublicApiKey<CS> {
168    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
169    where
170        S: Serializer,
171    {
172        ExportedData::<CS, _>::from_key(&self.0, ExportedDataType::PublicApiKey)
173            .serialize(serializer)
174    }
175}
176
177impl<'de, CS: CipherSuite> Deserialize<'de> for PublicApiKey<CS> {
178    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
179    where
180        D: Deserializer<'de>,
181    {
182        let data = ExportedData::<CS, SerdeOwnedKey<_>>::deserialize(deserializer)?;
183        if !data.is_type(ExportedDataType::PublicApiKey) {
184            Err(de::Error::custom(ImportError::InvalidContext))
185        } else {
186            Ok(Self(data.data.0))
187        }
188    }
189}
190
191impl<CS: CipherSuite> Identified for PublicApiKey<CS> {
192    type Id = ApiKeyId;
193
194    #[inline]
195    fn id(&self) -> Result<Self::Id, IdError> {
196        self.id()
197    }
198}
199
200// Allow repeated suffixes since different types will be added in
201// the future.
202#[allow(clippy::enum_variant_names)]
203#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
204enum ExportedDataType {
205    PublicApiKey,
206}
207
208/// Non-secret exported from an `Engine`.
209#[derive(Serialize, Deserialize)]
210#[serde(deny_unknown_fields)]
211struct ExportedData<CS, T>
212where
213    CS: CipherSuite,
214{
215    /// Uniquely idenitifies the chosen algorithms.
216    #[serde(bound = "CS: CipherSuite")]
217    oids: Oids<CS>,
218    /// Uniquely idenitifes the type of data.
219    name: ExportedDataType,
220    /// The exported data.
221    pub(crate) data: T,
222}
223
224impl<CS, T> ExportedData<CS, T>
225where
226    CS: CipherSuite,
227{
228    pub(crate) fn is_type(&self, name: ExportedDataType) -> bool {
229        self.name == name
230    }
231}
232
233impl<'a, CS, K: PublicKey> ExportedData<CS, SerdeBorrowedKey<'a, K>>
234where
235    CS: CipherSuite,
236{
237    pub(crate) fn from_key(pk: &'a K, name: ExportedDataType) -> Self {
238        Self {
239            oids: CS::OIDS,
240            name,
241            data: SerdeBorrowedKey(pk),
242        }
243    }
244}
245
246/// An owned [`PublicKey`] for deserializing.
247pub(crate) struct SerdeOwnedKey<K>(pub(crate) K);
248
249impl<'de, K: PublicKey> Deserialize<'de> for SerdeOwnedKey<K> {
250    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
251    where
252        D: Deserializer<'de>,
253    {
254        struct PkVisitor<K>(PhantomData<K>);
255
256        impl<'de, K: PublicKey> de::Visitor<'de> for PkVisitor<K> {
257            type Value = K;
258
259            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260                write!(f, "a public key")
261            }
262
263            fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
264            where
265                E: de::Error,
266            {
267                K::import(v).map_err(de::Error::custom)
268            }
269
270            fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
271            where
272                E: de::Error,
273            {
274                K::import(v).map_err(de::Error::custom)
275            }
276        }
277        let pk = deserializer.deserialize_bytes(PkVisitor::<K>(PhantomData))?;
278        Ok(SerdeOwnedKey(pk))
279    }
280}
281
282/// A borrowed [`PublicKey`] for serializing.
283struct SerdeBorrowedKey<'a, K>(&'a K);
284
285impl<K: PublicKey> Serialize for SerdeBorrowedKey<'_, K> {
286    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
287    where
288        S: Serializer,
289    {
290        serializer.serialize_bytes(self.0.export().borrow())
291    }
292}