Skip to content
This repository was archived by the owner on Sep 17, 2025. It is now read-only.

Commit efda32d

Browse files
Merge pull request #1 from osvauld/feature/refactor
Feature/refactor
2 parents e9eca66 + 178de7a commit efda32d

File tree

6 files changed

+459
-201
lines changed

6 files changed

+459
-201
lines changed

src/api/mod.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use reqwest::blocking::Client;
2+
use serde::{Deserialize, Serialize};
3+
use std::error::Error;
4+
5+
#[derive(Deserialize, Debug)]
6+
struct ChallengeResponse {
7+
data: ChallengeData,
8+
}
9+
10+
#[derive(Deserialize, Debug)]
11+
struct ChallengeData {
12+
challenge: String,
13+
}
14+
15+
#[derive(Serialize, Debug)]
16+
struct ChallengeRequest {
17+
publicKey: String,
18+
}
19+
20+
#[derive(Serialize, Debug)]
21+
struct AuthRequest {
22+
signature: String,
23+
publicKey: String,
24+
}
25+
26+
#[derive(Deserialize, Debug)]
27+
struct AuthResponse {
28+
data: AuthData,
29+
}
30+
31+
#[derive(Deserialize, Debug)]
32+
struct AuthData {
33+
token: String,
34+
}
35+
36+
#[derive(Deserialize, Debug)]
37+
pub struct GetEnvironmentFieldsByNameRow {
38+
id: String,
39+
pub fieldName: String,
40+
pub fieldValue: String,
41+
credentialId: String,
42+
}
43+
44+
#[derive(Deserialize, Debug)]
45+
struct GetEnvApiResponse {
46+
data: Vec<GetEnvironmentFieldsByNameRow>,
47+
}
48+
49+
pub fn fetch_challenge(base_url: &str, public_key: &str) -> Result<String, Box<dyn Error>> {
50+
let client = Client::new();
51+
let request_body = ChallengeRequest {
52+
publicKey: public_key.to_string(),
53+
};
54+
55+
let challenge_response: ChallengeResponse = client
56+
.post(format!("{}/user/challenge", base_url))
57+
.json(&request_body)
58+
.send()?
59+
.json()?;
60+
61+
Ok(challenge_response.data.challenge)
62+
}
63+
64+
pub fn verify_challenge(
65+
base_url: &str,
66+
signed_challenge: &str,
67+
public_key: &str,
68+
) -> Result<String, Box<dyn Error>> {
69+
let client = Client::new();
70+
let request_body = AuthRequest {
71+
signature: signed_challenge.to_string(),
72+
publicKey: public_key.to_string(),
73+
};
74+
75+
let auth_response: AuthResponse = client
76+
.post(format!("{}/user/verify", base_url))
77+
.json(&request_body)
78+
.send()?
79+
.json()?;
80+
81+
Ok(auth_response.data.token)
82+
}
83+
84+
pub fn get_environment_by_name(
85+
base_url: &str,
86+
env_name: &str,
87+
jwt_token: &str,
88+
) -> Result<Vec<GetEnvironmentFieldsByNameRow>, Box<dyn Error>> {
89+
let client = Client::new();
90+
let url = format!("{}/environment/{}", base_url, env_name);
91+
92+
let response: GetEnvApiResponse = client
93+
.get(&url)
94+
.header("Authorization", format!("Bearer {}", jwt_token))
95+
.send()?
96+
.json()?;
97+
98+
Ok(response.data)
99+
}

src/crypto/mod.rs

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
use base64::decode;
2+
use openpgp::cert::prelude::*;
3+
use openpgp::crypto::KeyPair;
4+
use openpgp::packet::key::SecretParts;
5+
use openpgp::packet::key::UnspecifiedRole;
6+
use openpgp::packet::Key;
7+
use openpgp::parse::{
8+
stream::{DecryptorBuilder, VerificationHelper},
9+
Parse,
10+
};
11+
use openpgp::policy::StandardPolicy as P;
12+
use openpgp::policy::{Policy, StandardPolicy};
13+
use openpgp::serialize::stream::*;
14+
use sequoia_openpgp as openpgp;
15+
use std::error::Error;
16+
use std::io::Cursor;
17+
use std::io::Write;
18+
19+
pub fn sign_challenge_with_key(
20+
challenge: &str,
21+
enc_private_key_base64: &str,
22+
) -> Result<String, Box<dyn Error>> {
23+
// Decode the base64-encoded private key
24+
let enc_private_key = decode(enc_private_key_base64)?;
25+
26+
// Load the private key
27+
let cert = openpgp::Cert::from_reader(&*enc_private_key)?;
28+
29+
// Prepare the helper with the private key
30+
let key_pair = cert
31+
.keys()
32+
.unencrypted_secret()
33+
.with_policy(&P::new(), None)
34+
.supported()
35+
.alive()
36+
.revoked(false)
37+
.for_signing()
38+
.next()
39+
.ok_or("No signing key found")?
40+
.key()
41+
.clone()
42+
.into_keypair()?;
43+
44+
// Sign the challenge
45+
let signed_challenge_base64 = sign_message_with_keypair(challenge, key_pair)?;
46+
47+
Ok(signed_challenge_base64)
48+
}
49+
50+
pub fn sign_message_with_keypair(message: &str, keypair: KeyPair) -> Result<String, String> {
51+
let mut signed_message = Vec::new();
52+
let message_writer = Message::new(&mut signed_message);
53+
54+
let mut signer = Signer::new(message_writer, keypair)
55+
.detached()
56+
.build()
57+
.map_err(|e| e.to_string())?;
58+
59+
signer
60+
.write_all(message.as_bytes())
61+
.map_err(|_| "Failed to write message to signer.")?;
62+
signer
63+
.finalize()
64+
.map_err(|_| "Failed to finalize signer.")?;
65+
66+
let mut armored_signature = Vec::new();
67+
let mut armor_writer =
68+
openpgp::armor::Writer::new(&mut armored_signature, openpgp::armor::Kind::Signature)
69+
.map_err(|e| e.to_string())?;
70+
71+
armor_writer
72+
.write_all(&signed_message)
73+
.map_err(|_| "Failed to write signature.")?;
74+
armor_writer
75+
.finalize()
76+
.map_err(|_| "Failed to finalize armored writer.")?;
77+
78+
let base64_encoded_signature = base64::encode(armored_signature);
79+
Ok(base64_encoded_signature)
80+
}
81+
82+
pub fn get_key_pair(
83+
private_key_b64: &str,
84+
) -> Result<Key<SecretParts, UnspecifiedRole>, Box<dyn Error>> {
85+
let private_key_bytes = decode(private_key_b64)?;
86+
let cert = Cert::from_bytes(&private_key_bytes)?;
87+
let p = &StandardPolicy::new();
88+
89+
// Get the secret key from the certificate for encryption without password
90+
let keypair = cert
91+
.keys()
92+
.with_policy(p, None)
93+
.secret()
94+
.for_storage_encryption()
95+
.nth(0)
96+
.ok_or_else(|| "No suitable key found in Cert.")?
97+
.key()
98+
.clone();
99+
100+
// The keypair now contains the decrypted secret key
101+
// You can use it to perform cryptographic operations
102+
103+
Ok(keypair)
104+
}
105+
106+
pub fn decrypt_message(
107+
sk: &Key<openpgp::packet::key::SecretParts, openpgp::packet::key::UnspecifiedRole>,
108+
text: &String,
109+
) -> openpgp::Result<String> {
110+
let p = StandardPolicy::new();
111+
let ciphertext = decode(text)?;
112+
let helper = Helper {
113+
secret: &sk,
114+
policy: &p,
115+
};
116+
117+
// Parse the message and create a decryptor with the helper.
118+
let mut decryptor = DecryptorBuilder::from_bytes(&ciphertext)?.with_policy(&p, None, helper)?;
119+
120+
// Read the decrypted data
121+
let mut plaintext = Cursor::new(Vec::new());
122+
123+
// Copy the decrypted data to the plaintext Vec<u8>
124+
std::io::copy(&mut decryptor, &mut plaintext)?;
125+
126+
// Get the plaintext Vec<u8> from the Cursor
127+
let plaintext = plaintext.into_inner();
128+
let plaintext = String::from_utf8(plaintext)?;
129+
130+
Ok(plaintext)
131+
}
132+
133+
struct Helper<'a> {
134+
secret: &'a Key<openpgp::packet::key::SecretParts, openpgp::packet::key::UnspecifiedRole>,
135+
policy: &'a dyn Policy,
136+
}
137+
138+
impl<'a> openpgp::parse::stream::DecryptionHelper for Helper<'a> {
139+
fn decrypt<D>(
140+
&mut self,
141+
pkesks: &[openpgp::packet::PKESK],
142+
_skesks: &[openpgp::packet::SKESK],
143+
sym_algo: Option<openpgp::types::SymmetricAlgorithm>,
144+
mut decrypt: D,
145+
) -> openpgp::Result<Option<openpgp::Fingerprint>>
146+
where
147+
D: FnMut(openpgp::types::SymmetricAlgorithm, &openpgp::crypto::SessionKey) -> bool,
148+
{
149+
// The secret key is already decrypted.
150+
let mut pair = KeyPair::from(self.secret.clone().into_keypair()?);
151+
152+
pkesks[0]
153+
.decrypt(&mut pair, sym_algo)
154+
.map(|(algo, session_key)| decrypt(algo, &session_key));
155+
156+
Ok(None)
157+
}
158+
}
159+
160+
impl<'a> VerificationHelper for Helper<'_> {
161+
fn get_certs(&mut self, _ids: &[openpgp::KeyHandle]) -> openpgp::Result<Vec<openpgp::Cert>> {
162+
Ok(Vec::new())
163+
}
164+
165+
fn check(
166+
&mut self,
167+
structure: openpgp::parse::stream::MessageStructure,
168+
) -> openpgp::Result<()> {
169+
for layer in structure.iter() {
170+
match layer {
171+
openpgp::parse::stream::MessageLayer::Compression { algo } => {
172+
// eprintln!("Compressed using {}", algo)
173+
}
174+
openpgp::parse::stream::MessageLayer::Encryption {
175+
sym_algo,
176+
aead_algo,
177+
} => {
178+
if let Some(aead_algo) = aead_algo {
179+
// eprintln!("Encrypted and protected using {}/{}", sym_algo, aead_algo);
180+
} else {
181+
// eprintln!("Encrypted using {}", sym_algo);
182+
}
183+
}
184+
openpgp::parse::stream::MessageLayer::SignatureGroup { ref results } => {
185+
for result in results {
186+
match result {
187+
Ok(openpgp::parse::stream::GoodChecksum { ka, .. }) => {
188+
// eprintln!("Good signature from {}", ka.cert());
189+
}
190+
Err(e) => eprintln!("Error: {:?}", e),
191+
}
192+
}
193+
}
194+
}
195+
}
196+
Ok(())
197+
}
198+
}

0 commit comments

Comments
 (0)