warg_protocol/
proto_envelope.rs1use super::registry::RegistryIndex;
2use anyhow::Error;
3use base64::{engine::general_purpose::STANDARD, Engine};
4use prost::Message;
5use serde::{Deserialize, Serialize};
6use serde_with::{base64::Base64, serde_as};
7use std::fmt;
8use thiserror::Error;
9use warg_crypto::{hash::AnyHashError, signing, Decode, Signable};
10use warg_protobuf::protocol as protobuf;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct PublishedProtoEnvelope<Contents> {
15 pub envelope: ProtoEnvelope<Contents>,
17 pub registry_index: RegistryIndex,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct ProtoEnvelope<Contents> {
26 contents: Contents,
28 content_bytes: Vec<u8>,
30 key_id: signing::KeyID,
32 signature: signing::Signature,
34}
35
36impl<Contents> ProtoEnvelope<Contents> {
37 pub fn signed_contents(
39 private_key: &signing::PrivateKey,
40 contents: Contents,
41 ) -> Result<Self, signing::SignatureError>
42 where
43 Contents: Signable,
44 {
45 let content_bytes: Vec<u8> = contents.encode();
46
47 let key_id = private_key.public_key().fingerprint();
48 let signature = contents.sign(private_key)?;
49 Ok(ProtoEnvelope {
50 contents,
51 content_bytes,
52 key_id,
53 signature,
54 })
55 }
56
57 pub fn content_bytes(&self) -> &[u8] {
59 &self.content_bytes
60 }
61
62 pub fn key_id(&self) -> &signing::KeyID {
63 &self.key_id
64 }
65
66 pub fn signature(&self) -> &signing::Signature {
67 &self.signature
68 }
69
70 pub fn to_protobuf(&self) -> Vec<u8> {
73 let proto_envelope = protobuf::Envelope {
74 contents: self.content_bytes.clone(),
75 key_id: self.key_id.to_string(),
76 signature: self.signature.to_string(),
77 };
78 proto_envelope.encode_to_vec()
79 }
80
81 pub fn from_protobuf(bytes: &[u8]) -> Result<Self, ParseEnvelopeError>
84 where
85 Contents: Decode,
86 {
87 let envelope = protobuf::Envelope::decode(bytes)?;
89 let contents = Contents::decode(&envelope.contents)?;
90
91 let key_id = envelope.key_id.into();
93 let signature = envelope.signature.parse()?;
94
95 Ok(ProtoEnvelope {
96 contents,
97 content_bytes: envelope.contents,
98 key_id,
99 signature,
100 })
101 }
102}
103
104impl<Content> AsRef<Content> for ProtoEnvelope<Content> {
105 fn as_ref(&self) -> &Content {
106 &self.contents
107 }
108}
109
110#[derive(Error, Debug)]
112pub enum ParseEnvelopeError {
113 #[error("failed to parse the outer envelope protobuf message")]
114 ProtobufEnvelope(#[from] prost::DecodeError),
115
116 #[error("failed to parse envelope contents from bytes")]
117 Contents(#[from] Error),
118
119 #[error("failed to parse envelope key id")]
120 KeyID(#[from] AnyHashError),
121
122 #[error("failed to parse envelope signature")]
123 Signature(#[from] signing::SignatureParseError),
124}
125
126#[serde_as]
127#[derive(Clone, Serialize, Deserialize)]
128#[serde(rename_all = "camelCase")]
129pub struct ProtoEnvelopeBody {
130 #[serde_as(as = "Base64")]
132 content_bytes: Vec<u8>,
133 key_id: signing::KeyID,
135 signature: signing::Signature,
137}
138
139impl<Content> TryFrom<ProtoEnvelopeBody> for ProtoEnvelope<Content>
140where
141 Content: Decode,
142{
143 type Error = Error;
144
145 fn try_from(value: ProtoEnvelopeBody) -> Result<Self, Self::Error> {
146 let contents = Content::decode(&value.content_bytes)?;
147 let envelope = ProtoEnvelope {
148 contents,
149 content_bytes: value.content_bytes,
150 key_id: value.key_id,
151 signature: value.signature,
152 };
153 Ok(envelope)
154 }
155}
156
157impl<Content> From<ProtoEnvelope<Content>> for ProtoEnvelopeBody {
158 fn from(value: ProtoEnvelope<Content>) -> Self {
159 ProtoEnvelopeBody {
160 content_bytes: value.content_bytes,
161 key_id: value.key_id,
162 signature: value.signature,
163 }
164 }
165}
166
167impl fmt::Debug for ProtoEnvelopeBody {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 f.debug_struct("ProtoEnvelopeBody")
170 .field("content_bytes", &STANDARD.encode(&self.content_bytes))
171 .field("key_id", &self.key_id)
172 .field("signature", &self.signature)
173 .finish()
174 }
175}
176
177#[serde_as]
178#[derive(Clone, Serialize, Deserialize)]
179#[serde(rename_all = "camelCase")]
180pub struct PublishedProtoEnvelopeBody {
181 #[serde(flatten)]
183 pub envelope: ProtoEnvelopeBody,
184 pub registry_index: RegistryIndex,
186}
187
188impl<Content> TryFrom<PublishedProtoEnvelopeBody> for PublishedProtoEnvelope<Content>
189where
190 Content: Decode,
191{
192 type Error = Error;
193
194 fn try_from(value: PublishedProtoEnvelopeBody) -> Result<Self, Self::Error> {
195 Ok(PublishedProtoEnvelope {
196 envelope: ProtoEnvelope::<Content>::try_from(value.envelope)?,
197 registry_index: value.registry_index,
198 })
199 }
200}
201
202impl<Content> From<PublishedProtoEnvelope<Content>> for PublishedProtoEnvelopeBody {
203 fn from(value: PublishedProtoEnvelope<Content>) -> Self {
204 PublishedProtoEnvelopeBody {
205 envelope: ProtoEnvelopeBody::from(value.envelope),
206 registry_index: value.registry_index,
207 }
208 }
209}
210
211impl fmt::Debug for PublishedProtoEnvelopeBody {
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 f.debug_struct("PublishedProtoEnvelopeBody")
214 .field(
215 "content_bytes",
216 &STANDARD.encode(&self.envelope.content_bytes),
217 )
218 .field("key_id", &self.envelope.key_id)
219 .field("signature", &self.envelope.signature)
220 .field("registry_index", &self.registry_index)
221 .finish()
222 }
223}