1use std::{format, sync::Arc};
2
3use aes_gcm::{AeadInPlace, KeyInit};
4use azure_identity::DefaultAzureCredentialBuilder;
5use azure_security_keyvault::prelude::{DecryptParameters, EncryptionAlgorithm};
6use serde::Deserialize;
7
8use base64::Engine;
9use typenum::U32;
10
11pub type SopsAES = aes_gcm::AesGcm<aes::Aes256, typenum::U32>;
12type UnsupportedVault = Vec<serde_json::Map<String, serde_json::Value>>;
13
14#[derive(Debug, Deserialize)]
15pub struct SopsFile {
16 sops: Sops,
17 #[serde(flatten)]
18 pub content: serde_json::Map<String, serde_json::Value>,
19}
20
21impl SopsFile {
22 pub async fn get_ciphers(&self) -> Vec<SopsAES> {
23 let mut ciphers: Vec<SopsAES> = Vec::new();
24
25 if let Some(ref vec_akvs) = self.sops.azure_kv {
27 let creds = Arc::new(
28 DefaultAzureCredentialBuilder::new()
29 .exclude_managed_identity_credential()
30 .build(),
31 );
32
33 for akv in vec_akvs {
34 let enc_key = base64::engine::general_purpose::URL_SAFE_NO_PAD
35 .decode(akv.enc.clone())
36 .expect("failed to debase64 encrypted master key");
37
38 let key_client =
39 azure_security_keyvault::KeyClient::new(&akv.vault_url, creds.clone())
40 .expect("failed to create a Key Client");
41
42 let params = DecryptParameters {
43 ciphertext: enc_key,
44 decrypt_parameters_encryption:
45 azure_security_keyvault::prelude::DecryptParametersEncryption::Rsa(
46 azure_security_keyvault::prelude::RsaDecryptParameters {
47 algorithm: EncryptionAlgorithm::RsaOaep256,
48 },
49 ),
50 };
51
52 let master_key = key_client
53 .decrypt(akv.name.clone(), params)
54 .await
55 .expect("master key decryption failed")
56 .result;
57
58 let cipher =
59 SopsAES::new_from_slice(&master_key).expect("failed to construct a cipher");
60
61 ciphers.push(cipher);
62 }
63 }
64
65 ciphers
66 }
67
68 pub fn get_content(&self, key: &str) -> EncryptedContent {
69 let enc_stuff = self
70 .content
71 .get(key)
72 .expect("failed to find the key from the file")
73 .as_str()
74 .expect("the value wasn't string");
75
76 let line_regex = regex::Regex::new(r"^ENC\[AES256_GCM,data:(?P<data>(.+)),iv:(?P<iv>(.+)),tag:(?P<tag>(.+)),type:(?P<type>(.+))\]").expect("could not compile regex");
77 let captures = line_regex.captures(enc_stuff).expect("bad format");
78
79 let data = base64::engine::general_purpose::STANDARD
80 .decode(&captures["data"])
81 .expect("failed to debase64 data");
82 let iv = base64::engine::general_purpose::STANDARD
83 .decode(&captures["iv"])
84 .expect("failed to debase64 iv");
85 let tag_raw = base64::engine::general_purpose::STANDARD
86 .decode(&captures["tag"])
87 .expect("failed to debase64 tag");
88
89 let nonce = aes_gcm::Nonce::from_slice(&iv);
90 let tag = aes_gcm::Tag::from_slice(&tag_raw);
91
92 EncryptedContent {
93 data,
94 nonce: *nonce,
95 tag: *tag,
96 path: key.to_string(),
97 }
98 }
99}
100
101pub struct EncryptedContent {
102 data: Vec<u8>,
103 nonce: aes_gcm::Nonce<U32>,
104 tag: aes_gcm::Tag,
105 path: String,
106}
107
108impl EncryptedContent {
109 pub fn decrypt(&self, ciphers: &[SopsAES]) -> String {
110 ciphers
111 .iter()
112 .find_map(|cipher| {
113 let mut buffer = self.data.clone();
114 match cipher.decrypt_in_place_detached(
115 &self.nonce,
116 format!("{}:", self.path).as_bytes(),
117 &mut buffer,
118 &self.tag,
119 ) {
120 Ok(_) => Some(String::from_utf8(buffer).expect("not utf-8 enough")),
121 Err(_) => None,
122 }
123 })
124 .expect("no keys found")
125 }
126}
127
128#[derive(Debug, Deserialize)]
129pub struct Sops {
130 kms: Option<UnsupportedVault>, gcp_kms: Option<UnsupportedVault>,
132 azure_kv: Option<Vec<AzureKV>>,
133 hc_vault: Option<UnsupportedVault>,
134 age: Option<UnsupportedVault>,
135 lastmodified: String, mac: String,
137 pgp: Option<UnsupportedVault>,
138 unencrypted_suffix: String,
139 version: String,
140}
141
142#[derive(Debug, Deserialize)]
143pub struct AzureKV {
144 vault_url: String,
145 name: String,
146 version: String,
147 created_at: String,
148 enc: String,
149}