• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / etl / 15843831769

24 Jun 2025 07:13AM UTC coverage: 61.288% (-0.3%) from 61.584%
15843831769

push

github

web-flow
feat(replicator): Add v2 pipeline in the replicator (#150)

452 of 832 new or added lines in 48 files covered. (54.33%)

22 existing lines in 14 files now uncovered.

5633 of 9191 relevant lines covered (61.29%)

30.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.45
/api/src/encryption.rs
1
use aws_lc_rs::{
2
    aead::{Aad, Nonce, RandomizedNonceKey, AES_256_GCM},
3
    rand::fill,
4
};
5
use base64::prelude::BASE64_STANDARD;
6
use base64::Engine;
7
use serde::{Deserialize, Serialize};
8
use std::string;
9
use thiserror::Error;
10

11
/// Errors that can occur during encryption operations.
12
#[derive(Debug, Error)]
13
pub enum EncryptionError {
14
    /// An unspecified error occurred while encrypting data.
15
    #[error("An unspecified error occurred while encrypting data")]
16
    Unspecified(#[from] aws_lc_rs::error::Unspecified),
17
}
18

19
/// Errors that can occur during decryption operations.
20
#[derive(Debug, Error)]
21
pub enum DecryptionError {
22
    /// An unspecified error occurred while decrypting data.
23
    #[error("An unspecified error occurred while decrypting data")]
24
    Unspecified(#[from] aws_lc_rs::error::Unspecified),
25

26
    /// Failed to decode base64 data during decryption.
27
    #[error("An error occurred while decoding BASE64 data for decryption: {0}")]
28
    Decode(#[from] base64::DecodeError),
29

30
    /// Failed to convert decrypted bytes to UTF-8 string.
31
    #[error("An error occurred while converting bytes to UTF-8 for decryption: {0}")]
32
    FromUtf8(#[from] string::FromUtf8Error),
33

34
    /// The key ID in the encrypted data did not match the expected key ID.
35
    #[error("There was a mismatch in the key id while decrypting data (got: {0}, expected: {1})")]
36
    MismatchedKeyId(u32, u32),
37
}
38

39
/// Trait for types that can be encrypted into another type.
40
pub trait Encrypt<T> {
41
    /// Encrypts `self` using the provided [`EncryptionKey`].
42
    fn encrypt(self, encryption_key: &EncryptionKey) -> Result<T, EncryptionError>;
43
}
44

45
/// Trait for types that can be decrypted into another type.
46
pub trait Decrypt<T> {
47
    /// Decrypts `self` using the provided [`EncryptionKey`].
48
    fn decrypt(self, encryption_key: &EncryptionKey) -> Result<T, DecryptionError>;
49
}
50

51
/// Holds an encryption key and its identifier.
52
pub struct EncryptionKey {
53
    /// Unique identifier for the key.
54
    pub id: u32,
55
    /// The key material used for encryption and decryption.
56
    pub key: RandomizedNonceKey,
57
}
58

59
/// Represents an encrypted value with its key ID and nonce.
60
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
61
pub struct EncryptedValue {
62
    /// Identifier of the key used for encryption.
63
    pub id: u32,
64
    /// Base64-encoded nonce used during encryption.
65
    pub nonce: String,
66
    /// Base64-encoded encrypted value.
67
    pub value: String,
68
}
69

70
/// Encrypts a string value using the provided [`EncryptionKey`].
71
///
72
/// The result is an [`EncryptedValue`] containing the key ID, base64-encoded nonce,
73
/// and base64-encoded ciphertext.
74
pub fn encrypt_text(
74✔
75
    value: String,
74✔
76
    encryption_key: &EncryptionKey,
74✔
77
) -> Result<EncryptedValue, EncryptionError> {
74✔
78
    let (encrypted_password, nonce) = encrypt(value.as_bytes(), &encryption_key.key)?;
74✔
79
    let encoded_encrypted_password = BASE64_STANDARD.encode(encrypted_password);
74✔
80
    let encoded_nonce = BASE64_STANDARD.encode(nonce.as_ref());
74✔
81

74✔
82
    Ok(EncryptedValue {
74✔
83
        id: encryption_key.id,
74✔
84
        nonce: encoded_nonce,
74✔
85
        value: encoded_encrypted_password,
74✔
86
    })
74✔
87
}
74✔
88

89
/// Decrypts an [`EncryptedValue`] using the provided [`EncryptionKey`].
90
///
91
/// Returns the original string if decryption succeeds. Fails if the key ID does not match or if
92
/// decoding or decryption fails.
93
pub fn decrypt_text(
13✔
94
    encrypted_value: EncryptedValue,
13✔
95
    encryption_key: &EncryptionKey,
13✔
96
) -> Result<String, DecryptionError> {
13✔
97
    if encrypted_value.id != encryption_key.id {
13✔
NEW
98
        return Err(DecryptionError::MismatchedKeyId(
×
NEW
99
            encrypted_value.id,
×
NEW
100
            encryption_key.id,
×
NEW
101
        ));
×
102
    }
13✔
103

104
    let encrypted_value_bytes = BASE64_STANDARD.decode(encrypted_value.value)?;
13✔
105
    let nonce = Nonce::try_assume_unique_for_key(&BASE64_STANDARD.decode(encrypted_value.nonce)?)?;
13✔
106

107
    let decrypted_value_bytes = decrypt(encrypted_value_bytes, nonce, &encryption_key.key)?;
13✔
108

109
    let decrypted_value = String::from_utf8(decrypted_value_bytes)?;
13✔
110

111
    Ok(decrypted_value)
13✔
112
}
13✔
113

114
/// Encrypts a byte slice using the given [`RandomizedNonceKey`].
115
///
116
/// Returns the ciphertext and the nonce used for encryption.
117
fn encrypt(
74✔
118
    plaintext: &[u8],
74✔
119
    key: &RandomizedNonceKey,
74✔
120
) -> Result<(Vec<u8>, Nonce), aws_lc_rs::error::Unspecified> {
74✔
121
    let mut in_out = plaintext.to_vec();
74✔
122
    let nonce = key.seal_in_place_append_tag(Aad::empty(), &mut in_out)?;
74✔
123

124
    Ok((in_out, nonce))
74✔
125
}
74✔
126

127
/// Decrypts a ciphertext using the given [`RandomizedNonceKey`] and [`Nonce`].
128
///
129
/// Returns the decrypted plaintext bytes.
130
fn decrypt(
13✔
131
    mut ciphertext: Vec<u8>,
13✔
132
    nonce: Nonce,
13✔
133
    key: &RandomizedNonceKey,
13✔
134
) -> Result<Vec<u8>, aws_lc_rs::error::Unspecified> {
13✔
135
    let plaintext = key.open_in_place(nonce, Aad::empty(), &mut ciphertext)?;
13✔
136

137
    Ok(plaintext.to_vec())
13✔
138
}
13✔
139

140
/// Generates a random [`RandomizedNonceKey`] of length `T` bytes for use with AES-256-GCM.
141
///
142
/// The key is filled with cryptographically secure random bytes.
143
///
144
/// # Panics
145
///
146
/// Panics if `T` does not match the required key length for the cipher.
147
pub fn generate_random_key<const T: usize>(
59✔
148
) -> Result<RandomizedNonceKey, aws_lc_rs::error::Unspecified> {
59✔
149
    let mut key_bytes = [0u8; T];
59✔
150
    fill(&mut key_bytes)?;
59✔
151

152
    let key = RandomizedNonceKey::new(&AES_256_GCM, &key_bytes)?;
59✔
153

154
    Ok(key)
59✔
155
}
59✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc