ockam_vault/storage/
secrets_repository_sql.rs

1use sqlx::encode::IsNull;
2use sqlx::error::BoxDynError;
3use sqlx::*;
4use sqlx_core::any::AnyArgumentBuffer;
5use std::sync::Arc;
6use tracing::debug;
7use zeroize::{Zeroize, ZeroizeOnDrop};
8
9use crate::storage::secrets_repository::SecretsRepository;
10use ockam_core::async_trait;
11use ockam_core::compat::vec::Vec;
12use ockam_core::errcode::{Kind, Origin};
13use ockam_core::Result;
14use ockam_node::database::AutoRetry;
15use ockam_node::database::{FromSqlxError, SqlxDatabase, ToVoid};
16
17use crate::{
18    AeadSecret, AeadSecretKeyHandle, ECDSASHA256CurveP256SecretKey, EdDSACurve25519SecretKey,
19    HandleToSecret, SigningSecret, SigningSecretKeyHandle, X25519SecretKey, X25519SecretKeyHandle,
20    AEAD_TYPE,
21};
22
23/// Implementation of a secrets repository using a SQL database
24#[derive(Clone)]
25pub struct SecretsSqlxDatabase {
26    database: SqlxDatabase,
27}
28
29impl SecretsSqlxDatabase {
30    /// Create a new database for secrets
31    pub fn new(database: SqlxDatabase) -> Self {
32        debug!("create a repository for secrets");
33        Self { database }
34    }
35
36    /// Create a repository
37    pub fn make_repository(database: SqlxDatabase) -> Arc<dyn SecretsRepository> {
38        if database.needs_retry() {
39            Arc::new(AutoRetry::new(Self::new(database)))
40        } else {
41            Arc::new(Self::new(database))
42        }
43    }
44
45    /// Create a new in-memory database for secrets
46    pub async fn create() -> Result<Self> {
47        Ok(Self::new(SqlxDatabase::in_memory("secrets").await?))
48    }
49}
50
51const ED_DSA_CURVE_25519: &str = "EdDSACurve25519";
52const EC_DSA_SHA256_CURVE_P256: &str = "ECDSASHA256CurveP256";
53
54#[async_trait]
55impl SecretsRepository for SecretsSqlxDatabase {
56    async fn store_signing_secret(
57        &self,
58        handle: &SigningSecretKeyHandle,
59        secret: SigningSecret,
60    ) -> Result<()> {
61        let secret_type: String = match handle {
62            SigningSecretKeyHandle::EdDSACurve25519(_) => ED_DSA_CURVE_25519.into(),
63            SigningSecretKeyHandle::ECDSASHA256CurveP256(_) => EC_DSA_SHA256_CURVE_P256.into(),
64        };
65
66        let query = query(
67            r#"
68            INSERT INTO signing_secret (handle, secret_type, secret)
69            VALUES ($1, $2, $3)
70            ON CONFLICT (handle)
71            DO UPDATE SET secret_type = $2, secret = $3"#,
72        )
73        .bind(handle)
74        .bind(secret_type)
75        .bind(secret);
76        query.execute(&*self.database.pool).await.void()
77    }
78
79    async fn delete_signing_secret(&self, handle: &SigningSecretKeyHandle) -> Result<bool> {
80        let query = query("DELETE FROM signing_secret WHERE handle = $1").bind(handle);
81        let res = query.execute(&*self.database.pool).await.into_core()?;
82
83        Ok(res.rows_affected() != 0)
84    }
85
86    async fn get_signing_secret(
87        &self,
88        handle: &SigningSecretKeyHandle,
89    ) -> Result<Option<SigningSecret>> {
90        let query =
91            query_as("SELECT handle, secret_type, secret FROM signing_secret WHERE handle = $1")
92                .bind(handle);
93        let row: Option<SigningSecretRow> = query
94            .fetch_optional(&*self.database.pool)
95            .await
96            .into_core()?;
97        Ok(row.map(|r| r.signing_secret()).transpose()?)
98    }
99
100    async fn get_signing_secret_handles(&self) -> Result<Vec<SigningSecretKeyHandle>> {
101        let query = query_as("SELECT handle, secret_type, secret FROM signing_secret");
102        let rows: Vec<SigningSecretRow> =
103            query.fetch_all(&*self.database.pool).await.into_core()?;
104        Ok(rows
105            .iter()
106            .map(|r| r.handle())
107            .collect::<Result<Vec<_>>>()?)
108    }
109
110    async fn store_x25519_secret(
111        &self,
112        handle: &X25519SecretKeyHandle,
113        secret: X25519SecretKey,
114    ) -> Result<()> {
115        let query = query(
116            r#"
117        INSERT INTO x25519_secret (handle, secret)
118        VALUES ($1, $2)
119        ON CONFLICT (handle)
120        DO UPDATE SET secret = $2"#,
121        )
122        .bind(handle)
123        .bind(secret);
124        query.execute(&*self.database.pool).await.void()
125    }
126
127    async fn delete_x25519_secret(&self, handle: &X25519SecretKeyHandle) -> Result<bool> {
128        let query = query("DELETE FROM x25519_secret WHERE handle = $1").bind(handle);
129        let res = query.execute(&*self.database.pool).await.into_core()?;
130
131        Ok(res.rows_affected() != 0)
132    }
133
134    async fn get_x25519_secret(
135        &self,
136        handle: &X25519SecretKeyHandle,
137    ) -> Result<Option<X25519SecretKey>> {
138        let query =
139            query_as("SELECT handle, secret FROM x25519_secret WHERE handle = $1").bind(handle);
140        let row: Option<X25519SecretRow> = query
141            .fetch_optional(&*self.database.pool)
142            .await
143            .into_core()?;
144        Ok(row.map(|r| r.x25519_secret()).transpose()?)
145    }
146
147    async fn get_x25519_secret_handles(&self) -> Result<Vec<X25519SecretKeyHandle>> {
148        let query = query_as("SELECT handle, secret FROM x25519_secret");
149        let rows: Vec<X25519SecretRow> = query.fetch_all(&*self.database.pool).await.into_core()?;
150        Ok(rows
151            .iter()
152            .map(|r| r.handle())
153            .collect::<Result<Vec<_>>>()?)
154    }
155
156    async fn store_aead_secret(
157        &self,
158        handle: &AeadSecretKeyHandle,
159        secret: AeadSecret,
160    ) -> Result<()> {
161        let query = query(
162            r#"
163                INSERT INTO aead_secret (handle, type, secret)
164                VALUES ($1, $2, $3)
165                ON CONFLICT (handle)
166                DO UPDATE SET type = $2, secret = $3"#,
167        )
168        .bind(handle)
169        .bind(AEAD_TYPE)
170        .bind(secret);
171        query.execute(&*self.database.pool).await.void()
172    }
173
174    async fn delete_aead_secret(&self, handle: &AeadSecretKeyHandle) -> Result<bool> {
175        let query = query("DELETE FROM aead_secret WHERE handle = $1").bind(handle);
176        let res = query.execute(&*self.database.pool).await.into_core()?;
177
178        Ok(res.rows_affected() != 0)
179    }
180
181    async fn get_aead_secret(&self, handle: &AeadSecretKeyHandle) -> Result<Option<AeadSecret>> {
182        let query = query_as("SELECT secret FROM aead_secret WHERE handle = $1 AND type = $2")
183            .bind(handle)
184            .bind(AEAD_TYPE);
185        let row: Option<AeadSecretRow> = query
186            .fetch_optional(&*self.database.pool)
187            .await
188            .into_core()?;
189        Ok(row.map(|r| r.aead_secret()).transpose()?)
190    }
191
192    async fn delete_all(&self) -> Result<()> {
193        let mut transaction = self.database.begin().await.into_core()?;
194        let query1 = query("DELETE FROM signing_secret");
195        query1.execute(&mut *transaction).await.void()?;
196
197        let query2 = query("DELETE FROM x25519_secret");
198        query2.execute(&mut *transaction).await.void()?;
199
200        let query3 = query("DELETE FROM aead_secret");
201        query3.execute(&mut *transaction).await.void()?;
202
203        transaction.commit().await.void()
204    }
205}
206
207impl Type<Any> for SigningSecret {
208    fn type_info() -> <Any as Database>::TypeInfo {
209        <Vec<u8> as Type<Any>>::type_info()
210    }
211}
212
213impl Encode<'_, Any> for SigningSecret {
214    fn encode_by_ref(&self, buf: &mut AnyArgumentBuffer) -> Result<IsNull, BoxDynError> {
215        <Vec<u8> as Encode<'_, Any>>::encode_by_ref(&self.key().to_vec(), buf)
216    }
217}
218
219impl Type<Any> for SigningSecretKeyHandle {
220    fn type_info() -> <Any as Database>::TypeInfo {
221        <HandleToSecret as Type<Any>>::type_info()
222    }
223}
224
225impl Encode<'_, Any> for SigningSecretKeyHandle {
226    fn encode_by_ref(&self, buf: &mut AnyArgumentBuffer) -> Result<IsNull, BoxDynError> {
227        <HandleToSecret as Encode<'_, Any>>::encode_by_ref(self.handle(), buf)
228    }
229}
230
231impl Type<Any> for HandleToSecret {
232    fn type_info() -> <Any as Database>::TypeInfo {
233        <Vec<u8> as Type<Any>>::type_info()
234    }
235}
236
237impl Encode<'_, Any> for HandleToSecret {
238    fn encode_by_ref(&self, buf: &mut AnyArgumentBuffer) -> Result<IsNull, BoxDynError> {
239        <Vec<u8> as Encode<'_, Any>>::encode_by_ref(self.value(), buf)
240    }
241}
242
243impl Type<Any> for X25519SecretKeyHandle {
244    fn type_info() -> <Any as Database>::TypeInfo {
245        <HandleToSecret as Type<Any>>::type_info()
246    }
247}
248
249impl Encode<'_, Any> for X25519SecretKeyHandle {
250    fn encode_by_ref(&self, buf: &mut AnyArgumentBuffer) -> Result<IsNull, BoxDynError> {
251        <HandleToSecret as Encode<'_, Any>>::encode_by_ref(&self.0, buf)
252    }
253}
254
255impl Type<Any> for AeadSecretKeyHandle {
256    fn type_info() -> <Any as Database>::TypeInfo {
257        <HandleToSecret as Type<Any>>::type_info()
258    }
259}
260
261impl Encode<'_, Any> for AeadSecretKeyHandle {
262    fn encode_by_ref(&self, buf: &mut AnyArgumentBuffer) -> Result<IsNull, BoxDynError> {
263        <HandleToSecret as Encode<'_, Any>>::encode_by_ref(&self.0 .0, buf)
264    }
265}
266
267impl Type<Any> for X25519SecretKey {
268    fn type_info() -> <Any as Database>::TypeInfo {
269        <Vec<u8> as Type<Any>>::type_info()
270    }
271}
272
273impl Encode<'_, Any> for X25519SecretKey {
274    fn encode_by_ref(&self, buf: &mut AnyArgumentBuffer) -> Result<IsNull, BoxDynError> {
275        <Vec<u8> as Encode<'_, Any>>::encode_by_ref(&self.key().to_vec(), buf)
276    }
277}
278
279impl Type<Any> for AeadSecret {
280    fn type_info() -> <Any as Database>::TypeInfo {
281        <Vec<u8> as Type<Any>>::type_info()
282    }
283}
284
285impl Encode<'_, Any> for AeadSecret {
286    fn encode_by_ref(&self, buf: &mut AnyArgumentBuffer) -> Result<IsNull, BoxDynError> {
287        <Vec<u8> as Encode<'_, Any>>::encode_by_ref(&self.0.to_vec(), buf)
288    }
289}
290
291#[derive(FromRow, Zeroize, ZeroizeOnDrop)]
292struct SigningSecretRow {
293    handle: Vec<u8>,
294    secret_type: String,
295    secret: Vec<u8>,
296}
297
298impl SigningSecretRow {
299    fn signing_secret(&self) -> Result<SigningSecret> {
300        let secret = self.secret.clone().try_into().map_err(|_| {
301            ockam_core::Error::new(
302                Origin::Api,
303                Kind::Serialization,
304                "cannot convert a signing secret to [u8; 32]",
305            )
306        })?;
307        match self.secret_type.as_str() {
308            "EdDSACurve25519" => Ok(SigningSecret::EdDSACurve25519(
309                EdDSACurve25519SecretKey::new(secret),
310            )),
311            "ECDSASHA256CurveP256" => Ok(SigningSecret::ECDSASHA256CurveP256(
312                ECDSASHA256CurveP256SecretKey::new(secret),
313            )),
314            _ => Err(ockam_core::Error::new(
315                Origin::Api,
316                Kind::Serialization,
317                "cannot deserialize a signing secret",
318            )),
319        }
320    }
321
322    fn handle(&self) -> Result<SigningSecretKeyHandle> {
323        match self.secret_type.as_str() {
324            "EdDSACurve25519" => Ok(SigningSecretKeyHandle::EdDSACurve25519(
325                HandleToSecret::new(self.handle.clone()),
326            )),
327            "ECDSASHA256CurveP256" => Ok(SigningSecretKeyHandle::ECDSASHA256CurveP256(
328                HandleToSecret::new(self.handle.clone()),
329            )),
330            _ => Err(ockam_core::Error::new(
331                Origin::Api,
332                Kind::Serialization,
333                "cannot deserialize a signing secret handle",
334            )),
335        }
336    }
337}
338
339#[derive(FromRow, Zeroize, ZeroizeOnDrop)]
340struct X25519SecretRow {
341    handle: Vec<u8>,
342    secret: Vec<u8>,
343}
344
345impl X25519SecretRow {
346    fn x25519_secret(&self) -> Result<X25519SecretKey> {
347        let secret = self.secret.as_slice().try_into().map_err(|_| {
348            ockam_core::Error::new(
349                Origin::Api,
350                Kind::Serialization,
351                "cannot convert a X25519 secret to [u8; 32]",
352            )
353        })?;
354        Ok(X25519SecretKey::new(secret))
355    }
356
357    fn handle(&self) -> Result<X25519SecretKeyHandle> {
358        Ok(X25519SecretKeyHandle(HandleToSecret::new(
359            self.handle.clone(),
360        )))
361    }
362}
363
364#[derive(FromRow, Zeroize, ZeroizeOnDrop)]
365struct AeadSecretRow {
366    secret: Vec<u8>,
367}
368
369impl AeadSecretRow {
370    fn aead_secret(&self) -> Result<AeadSecret> {
371        let secret = self.secret.as_slice().try_into().map_err(|_| {
372            ockam_core::Error::new(
373                Origin::Api,
374                Kind::Serialization,
375                "cannot convert an AEAD secret to array",
376            )
377        })?;
378
379        Ok(AeadSecret(secret))
380    }
381}
382
383#[cfg(test)]
384mod test {
385    use super::*;
386
387    use ockam_core::compat::sync::Arc;
388
389    #[tokio::test]
390    async fn test_signing_secrets_repository() -> Result<()> {
391        let repository = create_repository().await?;
392
393        let handle1 =
394            SigningSecretKeyHandle::ECDSASHA256CurveP256(HandleToSecret::new(vec![1, 2, 3]));
395        let secret1 =
396            SigningSecret::ECDSASHA256CurveP256(ECDSASHA256CurveP256SecretKey::new([1; 32]));
397
398        let handle2 = SigningSecretKeyHandle::EdDSACurve25519(HandleToSecret::new(vec![4, 5, 6]));
399        let secret2 = SigningSecret::EdDSACurve25519(EdDSACurve25519SecretKey::new([1; 32]));
400
401        repository
402            .store_signing_secret(&handle1, secret1.clone())
403            .await?;
404        repository
405            .store_signing_secret(&handle2, secret2.clone())
406            .await?;
407
408        let result = repository.get_signing_secret(&handle1).await?;
409        assert!(result == Some(secret1));
410
411        let result = repository.get_signing_secret_handles().await?;
412        assert_eq!(result, vec![handle1.clone(), handle2]);
413
414        repository.delete_signing_secret(&handle1).await?;
415
416        let result = repository.get_signing_secret(&handle1).await?;
417        assert!(result.is_none());
418
419        Ok(())
420    }
421
422    #[tokio::test]
423    async fn test_x25519_secrets_repository() -> Result<()> {
424        let repository = create_repository().await?;
425
426        let handle1 = X25519SecretKeyHandle(HandleToSecret::new(vec![1, 2, 3]));
427        let secret1 = X25519SecretKey::new([1; 32]);
428
429        let handle2 = X25519SecretKeyHandle(HandleToSecret::new(vec![4, 5, 6]));
430        let secret2 = X25519SecretKey::new([1; 32]);
431
432        repository
433            .store_x25519_secret(&handle1, secret1.clone())
434            .await?;
435        repository
436            .store_x25519_secret(&handle2, secret2.clone())
437            .await?;
438
439        let result = repository.get_x25519_secret(&handle1).await?;
440        assert!(result == Some(secret1));
441
442        let result = repository.get_x25519_secret_handles().await?;
443        assert_eq!(result, vec![handle1.clone(), handle2]);
444
445        repository.delete_x25519_secret(&handle1).await?;
446
447        let result = repository.get_x25519_secret(&handle1).await?;
448        assert!(result.is_none());
449
450        Ok(())
451    }
452
453    #[tokio::test]
454    async fn test_aead_secrets_repository() -> Result<()> {
455        let repository = create_repository().await?;
456
457        let handle1 = AeadSecretKeyHandle::new(HandleToSecret::new(vec![1, 2, 3]));
458        let secret1 = AeadSecret([1; 32]);
459
460        let handle2 = AeadSecretKeyHandle::new(HandleToSecret::new(vec![4, 5, 6]));
461        let secret2 = AeadSecret([2; 32]);
462
463        repository
464            .store_aead_secret(&handle1, secret1.clone())
465            .await?;
466        repository
467            .store_aead_secret(&handle2, secret2.clone())
468            .await?;
469
470        let result = repository.get_aead_secret(&handle1).await?;
471        assert!(result == Some(secret1));
472
473        let result = repository.get_aead_secret(&handle2).await?;
474        assert!(result == Some(secret2));
475
476        repository.delete_aead_secret(&handle1).await?;
477
478        let result = repository.get_aead_secret(&handle1).await?;
479        assert!(result.is_none());
480
481        Ok(())
482    }
483
484    // HELPERS
485    async fn create_repository() -> Result<Arc<dyn SecretsRepository>> {
486        Ok(Arc::new(SecretsSqlxDatabase::create().await?))
487    }
488}