Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add symmetric encryption methods #7

Merged
merged 31 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c8dd9b0
Encryption errors
DanielHougaard Jan 3, 2024
efd08d8
Create client_cryptography.rs
DanielHougaard Jan 3, 2024
edecd47
Create symmetric key
DanielHougaard Jan 3, 2024
a7a98ac
Decrypt symmetric
DanielHougaard Jan 3, 2024
cca07d0
Create encrypt_symmetric.rs
DanielHougaard Jan 3, 2024
98ed856
Create mod.rs
DanielHougaard Jan 3, 2024
6b31098
Update mod.rs
DanielHougaard Jan 3, 2024
e003d2f
Create cryptography.rs
DanielHougaard Jan 3, 2024
721aa3c
Fixed decrypt
DanielHougaard Jan 3, 2024
e43f846
Update decrypt_symmetric.rs
DanielHougaard Jan 3, 2024
86526f3
Update cryptography.rs
DanielHougaard Jan 3, 2024
91dc719
Added commands to command handler
DanielHougaard Jan 4, 2024
c5cd11c
Added new commands to command enum
DanielHougaard Jan 4, 2024
2d2ddc7
Exported new useful types
DanielHougaard Jan 4, 2024
1ee56ec
Added new methods
DanielHougaard Jan 4, 2024
0a13d79
Fixed type issue
DanielHougaard Jan 4, 2024
8518fcf
Fixed type issue
DanielHougaard Jan 4, 2024
edc2d8c
Fixed type issue
DanielHougaard Jan 4, 2024
2c14ad5
Fixed type issue
DanielHougaard Jan 4, 2024
ada6d6e
Better structure
DanielHougaard Jan 4, 2024
e0276ff
Update cryptography.rs
DanielHougaard Jan 4, 2024
70be2f9
Schema types
DanielHougaard Jan 4, 2024
ca5e346
Added new methods to java sdk
DanielHougaard Jan 4, 2024
e997d3a
Added new methods to js sdk
DanielHougaard Jan 4, 2024
6ac2924
Made encrypt/decrypt backwards-compatible with existing SDK encryptio…
DanielHougaard Jan 4, 2024
0d6d598
Cleaned up decryption to use ? operator
DanielHougaard Jan 4, 2024
0345f48
Cleaned up encryption to use ? operator
DanielHougaard Jan 4, 2024
f064ba7
Improved node examples
DanielHougaard Jan 4, 2024
e91f00c
Update tsconfig.json
DanielHougaard Jan 4, 2024
14ac975
Removed some logs
DanielHougaard Jan 4, 2024
1836360
Update index.ts
DanielHougaard Jan 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@
"./crates/infisical-c/Cargo.toml",
"./crates/infisical-py/Cargo.toml"
],
"cSpell.words": ["dotenv", "infisical", "libinfisical", "openapi", "openapitools", "quicktype", "reqwest"],
"cSpell.words": [
"ciphertext",
"dotenv",
"indicies",
"infisical",
"libinfisical",
"openapi",
"openapitools",
"quicktype",
"reqwest",
"rngs"
],
"java.configuration.updateBuildConfiguration": "interactive"
}
12 changes: 12 additions & 0 deletions crates/infisical-json/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,23 @@ impl Client {
};

match cmd {
// Infisical secrets
Command::GetSecret(req) => self.0.secrets().get(&req).await.into_string(),
Command::ListSecrets(req) => self.0.secrets().list(&req).await.into_string(),
Command::CreateSecret(req) => self.0.secrets().create(&req).await.into_string(),
Command::UpdateSecret(req) => self.0.secrets().update(&req).await.into_string(),
Command::DeleteSecret(req) => self.0.secrets().delete(&req).await.into_string(),

// Symmetric cryptography
Command::DecryptSymmetric(req) => {
self.0.cryptography().decrypt_symmetric(&req).into_string()
}
Command::EncryptSymmetric(req) => {
self.0.cryptography().encrypt_symmetric(&req).into_string()
}
Command::CreateSymmetricKey(_) => {
self.0.cryptography().create_symmetric_key().into_string()
}
}
}

Expand Down
21 changes: 17 additions & 4 deletions crates/infisical-json/src/command.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
use infisical::manager::{
client_secrets::{CreateSecretOptions, GetSecretOptions, UpdateSecretOptions},
secrets::{DeleteSecretOptions, ListSecretsOptions},
use infisical::manager::secrets::{
CreateSecretOptions, DeleteSecretOptions, GetSecretOptions, ListSecretsOptions,
UpdateSecretOptions,
};

use infisical::manager::cryptography::{DecryptSymmetricOptions, EncryptSymmetricOptions};

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, JsonSchema, Debug)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
// QuickType (our type generator), won't recognize the CreateSymmetricKey command unless it has an input. Super annoying, and this is quite a hacky workaround.
// This should be revised in the future.
pub struct ArbitraryOptions {
pub data: String,
}

// We expand this later
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub enum Command {
GetSecret(GetSecretOptions),
ListSecrets(ListSecretsOptions),
CreateSecret(CreateSecretOptions),
UpdateSecret(UpdateSecretOptions),
DeleteSecret(DeleteSecretOptions),

CreateSymmetricKey(ArbitraryOptions),
EncryptSymmetric(EncryptSymmetricOptions),
DecryptSymmetric(DecryptSymmetricOptions),
}
8 changes: 7 additions & 1 deletion crates/infisical-py/infisical_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,10 @@
from .schemas import CreateSecretOptions as CreateSecretOptions
from .schemas import ListSecretsOptions as ListSecretsOptions
from .schemas import ClientSettings as ClientSettings
from .schemas import SecretElement as SecretElement
from .schemas import SecretElement as SecretElement

from .schemas import EncryptSymmetricOptions as EncryptSymmetricOptions
from .schemas import EncryptSymmetricResponse as EncryptSymmetricResponse

from .schemas import DecryptSymmetricOptions as DecryptSymmetricOptions
from .schemas import DecryptSymmetricResponse as DecryptSymmetricResponse
26 changes: 25 additions & 1 deletion crates/infisical-py/infisical_client/infisical_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
from .schemas import UpdateSecretOptions, ResponseForUpdateSecretResponse
from .schemas import DeleteSecretOptions, ResponseForDeleteSecretResponse
from .schemas import CreateSecretOptions, ResponseForCreateSecretResponse

from .schemas import EncryptSymmetricOptions, EncryptSymmetricResponse, ResponseForEncryptSymmetricResponse
from .schemas import DecryptSymmetricOptions, DecryptSymmetricResponse, ResponseForDecryptSymmetricResponse

from .schemas import ArbitraryOptions, ResponseForCreateSymmetricKeyResponse

import infisical_py
import os

Expand Down Expand Up @@ -58,4 +64,22 @@ def deleteSecret(self, options: DeleteSecretOptions) -> SecretElement:
def createSecret(self, options: CreateSecretOptions) -> SecretElement:
result = self._run_command(Command(create_secret=options))

return ResponseForCreateSecretResponse.from_dict(result).data.secret
return ResponseForCreateSecretResponse.from_dict(result).data.secret

def createSymmetricKey(self) -> str:

arbitraryOptions = ArbitraryOptions(data="")

result = self._run_command(Command(create_symmetric_key=arbitraryOptions))

return ResponseForCreateSymmetricKeyResponse.from_dict(result).data.key

def encryptSymmetric(self, options: EncryptSymmetricOptions) -> EncryptSymmetricResponse:
result = self._run_command(Command(encrypt_symmetric=options))

return ResponseForEncryptSymmetricResponse.from_dict(result).data

def decryptSymmetric(self, options: DecryptSymmetricOptions) -> str:
result = self._run_command(Command(decrypt_symmetric=options))

return ResponseForDecryptSymmetricResponse.from_dict(result).data.decrypted
1 change: 1 addition & 0 deletions crates/infisical/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@ env_logger = "0.10.1"
seeded-random = "0.6.0"
serial_test = "2.0.0"
dotenv = "0.15.0"
aes-gcm = "0.10.3"
10 changes: 0 additions & 10 deletions crates/infisical/src/api/secrets/update_secret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use crate::error::api_error_handler;
use crate::helper::build_base_request;
use crate::manager::secrets::{UpdateSecretOptions, UpdateSecretResponse};
use crate::{error::Result, Client};
use log::debug;
use reqwest::StatusCode;

pub async fn update_secret_request(
Expand Down Expand Up @@ -34,15 +33,6 @@ pub async fn update_secret_request(
Err(e) => return Err(e),
};

let token = match client.auth.access_token {
Some(ref token) => format!("Bearer {}", token),
None => "".to_string(),
};

debug!("Creating secret with token: {}", token);
debug!("Creating secret with JSON body: {:?}", json);
debug!("Creating secret with url: {}", base_url);

let response = request.json(json).send().await?;
let status = response.status();

Expand Down
9 changes: 9 additions & 0 deletions crates/infisical/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ pub enum Error {
#[error("Something unexpected went wrong.")]
UnknownError,

#[error("Failed to create symmetric key: {}", .message)]
CreateSymmetricKeyError { message: String },

#[error("Failed to encrypt symmetric key: {}", .message)]
EncryptSymmetricKeyError { message: String },

#[error("Failed to decrypt symmetric key: {}", .message)]
DecryptSymmetricKeyError { message: String },

#[error("Missing access token.")]
MissingAccessToken,

Expand Down
40 changes: 40 additions & 0 deletions crates/infisical/src/manager/client_cryptography.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use crate::{error::Result, Client};

// DELETE SECRET
use super::cryptography::{
decrypt_symmetric::{decrypt_symmetric, DecryptSymmetricOptions},
encrypt_symmetric::{encrypt_symmetric, EncryptSymmetricOptions, EncryptSymmetricResponse},
CreateSymmetricKeyResponse, DecryptSymmetricResponse,
};
pub use crate::manager::cryptography::create_symmetric_key::create_symmetric_key;

#[allow(dead_code)]
pub struct ClientCryptography<'a> {
pub(crate) client: &'a mut crate::Client,
}

impl<'a> ClientCryptography<'a> {
pub fn create_symmetric_key(&'a mut self) -> Result<CreateSymmetricKeyResponse> {
create_symmetric_key()
}

pub fn encrypt_symmetric(
&'a mut self,
input: &EncryptSymmetricOptions,
) -> Result<EncryptSymmetricResponse> {
encrypt_symmetric(input)
}

pub fn decrypt_symmetric(
&'a mut self,
input: &DecryptSymmetricOptions,
) -> Result<DecryptSymmetricResponse> {
decrypt_symmetric(input)
}
}

impl<'a> Client {
pub fn cryptography(&'a mut self) -> ClientCryptography<'a> {
ClientCryptography { client: self }
}
}
26 changes: 26 additions & 0 deletions crates/infisical/src/manager/cryptography/create_symmetric_key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use crate::error::Result;
use base64::engine::Engine;
use rand::Rng;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

macro_rules! b64_encode {
($key:expr) => {
base64::engine::general_purpose::STANDARD.encode($key)
};
}

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct CreateSymmetricKeyResponse {
pub key: String,
}

pub fn create_symmetric_key() -> Result<CreateSymmetricKeyResponse> {
// Generate a 256-bit key (32 bytes * 8 bits/byte)
let key: Vec<u8> = rand::thread_rng().gen::<[u8; 32]>().to_vec();

let encoded_key = b64_encode!(key);

return Ok(CreateSymmetricKeyResponse { key: encoded_key });
}
71 changes: 71 additions & 0 deletions crates/infisical/src/manager/cryptography/decrypt_symmetric.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use crate::error::{Error, Result};
use aes::cipher::generic_array::GenericArray;
use aes_gcm::{
aead::{Aead, KeyInit},
Aes256Gcm,
};
use base64::engine::Engine;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; // Assuming you are using the 'aes-gcm' crate

macro_rules! b64_decode {
($key:expr) => {
base64::engine::general_purpose::STANDARD.decode($key)
};
}

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct DecryptSymmetricOptions {
pub key: String,
pub ciphertext: String,
pub iv: String,
pub tag: String,
}

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct DecryptSymmetricResponse {
pub decrypted: String,
}

pub fn decrypt_symmetric(input: &DecryptSymmetricOptions) -> Result<DecryptSymmetricResponse> {
let decoded_tag = b64_decode!(&input.tag).map_err(|e| Error::DecryptSymmetricKeyError {
message: e.to_string(),
})?;
let decoded_key = b64_decode!(&input.key).map_err(|e| Error::DecryptSymmetricKeyError {
message: e.to_string(),
})?;
let iv = b64_decode!(&input.iv).map_err(|e| Error::DecryptSymmetricKeyError {
message: e.to_string(),
})?;
let mut decoded_ciphertext =
b64_decode!(&input.ciphertext).map_err(|e| Error::DecryptSymmetricKeyError {
message: e.to_string(),
})?;

// We modify the ciphertext a little bit here to remove the pre-existing tag, and append the tag that was provided as a parameter.
//decoded_ciphertext.truncate(decoded_ciphertext.len() - 16);
decoded_ciphertext.extend_from_slice(&decoded_tag);

let nonce = GenericArray::from_slice(&iv);

let cipher =
Aes256Gcm::new_from_slice(&decoded_key).map_err(|e| Error::DecryptSymmetricKeyError {
message: e.to_string(),
})?;

let plaintext_bytes = cipher
.decrypt(nonce, decoded_ciphertext.as_ref())
.map_err(|e| Error::DecryptSymmetricKeyError {
message: e.to_string(),
})?;

return Ok(DecryptSymmetricResponse {
decrypted: String::from_utf8(plaintext_bytes)
.map_err(|e| Error::DecryptSymmetricKeyError {
message: e.to_string(),
})
.expect("Failed to convert bytes to string."),
});
}
79 changes: 79 additions & 0 deletions crates/infisical/src/manager/cryptography/encrypt_symmetric.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use crate::error::{Error, Result};
use aes_gcm::{
aead::{Aead, KeyInit},
Aes256Gcm,
};
use base64::engine::Engine;
use rand::{rngs::OsRng, RngCore};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

macro_rules! b64_encode {
($key:expr) => {
base64::engine::general_purpose::STANDARD.encode($key)
};
}

macro_rules! b64_decode {
($key:expr) => {
base64::engine::general_purpose::STANDARD.decode($key)
};
}

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct EncryptSymmetricOptions {
pub key: String,
pub plaintext: String,
}

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct EncryptSymmetricResponse {
pub ciphertext: String,
pub iv: String,
pub tag: String,
}

pub fn encrypt_symmetric(input: &EncryptSymmetricOptions) -> Result<EncryptSymmetricResponse> {
let key = &input.key;
let plaintext = &input.plaintext;

// Generate a random IV
let mut iv = [0u8; 12];
OsRng.fill_bytes(&mut iv);

let decoded_key = b64_decode!(key).map_err(|e| Error::EncryptSymmetricKeyError {
message: e.to_string(),
})?;

// Create a new AES256-GCM instance
let cipher = Aes256Gcm::new_from_slice(decoded_key.as_slice()).map_err(|e| {
Error::EncryptSymmetricKeyError {
message: e.to_string(),
}
})?;

let ciphertext_r = &cipher
.encrypt(&iv.into(), plaintext.as_bytes())
.map_err(|e| Error::EncryptSymmetricKeyError {
message: e.to_string(),
});

let mut ciphertext = ciphertext_r.as_ref().unwrap().clone();

// we need to take the last 16 bytes of the ciphertext and remove it from the ciphertext, and append it to the tag.

let encryption_tag = &ciphertext.clone()[&ciphertext.clone().len() - 16..]; // This line is a bit confusing, but it basically takes the last 16 bytes of the ciphertext and stores it in a variable.
ciphertext.truncate(ciphertext.len() - 16); // This line removes the last 16 bytes of the ciphertext.

let encoded_ciphertext = b64_encode!(&ciphertext.clone());
let encoded_iv = b64_encode!(iv);
let encoded_tag = b64_encode!(encryption_tag);

return Ok(EncryptSymmetricResponse {
ciphertext: encoded_ciphertext,
iv: encoded_iv,
tag: encoded_tag,
});
}
Loading
Loading