Skip to main content

crev_data/
id.rs

1use crate::{
2    Url,
3    proof::{self, ContentExt, OverrideItem},
4};
5use crev_common::{
6    self,
7    serde::{as_base64, from_base64},
8};
9use derive_builder::Builder;
10use ed25519_dalek::{Signer, SigningKey, Verifier, VerifyingKey};
11use rand::rngs::OsRng;
12use serde::{Deserialize, Serialize};
13use std::{convert::TryFrom, fmt};
14
15#[derive(Clone, Debug, Serialize, Deserialize)]
16pub enum IdType {
17    #[serde(rename = "crev")]
18    Crev,
19}
20
21#[derive(Debug, thiserror::Error)]
22pub enum IdError {
23    #[error("wrong length of crev id, expected 32 bytes, got {}", _0)]
24    WrongIdLength(usize),
25    #[error("Invalid CrevId: {}", _0)]
26    InvalidCrevId(Box<str>),
27    #[error("Invalid signature: {}", _0)]
28    InvalidSignature(Box<str>),
29    #[error("Invalid public key: {}", _0)]
30    InvalidPublicKey(Box<str>),
31    #[error("Invalid secret key: {}", _0)]
32    InvalidSecretKey(Box<str>),
33}
34
35impl fmt::Display for IdType {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        use IdType::Crev;
38        f.write_str(match self {
39            Crev => "crev",
40        })
41    }
42}
43
44/// An Id supported by `crev` system
45///
46/// Right now it's only native `CrevID`, but in future at least GPG
47/// should be supported.
48#[derive(Clone, Serialize, Deserialize, Hash, PartialEq, Eq, PartialOrd, Ord)]
49#[serde(tag = "id-type")]
50pub enum Id {
51    #[serde(rename = "crev")]
52    Crev {
53        #[serde(serialize_with = "as_base64", deserialize_with = "from_base64")]
54        id: Vec<u8>,
55    },
56}
57
58impl fmt::Debug for Id {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        match self {
61            Id::Crev { id } => f.write_str(&crev_common::base64_encode(id)),
62        }
63    }
64}
65
66impl fmt::Display for Id {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        match self {
69            Id::Crev { id } => f.write_str(&crev_common::base64_encode(id)),
70        }
71    }
72}
73
74impl Id {
75    pub fn new_crev(bytes: Vec<u8>) -> Result<Self, IdError> {
76        if bytes.len() != 32 {
77            return Err(IdError::WrongIdLength(bytes.len()));
78        }
79        Ok(Id::Crev { id: bytes })
80    }
81
82    pub fn crevid_from_str(s: &str) -> Result<Self, IdError> {
83        let bytes = crev_common::base64_decode(s)
84            .map_err(|e| IdError::InvalidCrevId(e.to_string().into()))?;
85        Self::new_crev(bytes)
86    }
87
88    pub fn verify_signature(&self, content: &[u8], sig_str: &str) -> Result<(), IdError> {
89        match self {
90            Id::Crev { id } => {
91                let pubkey = VerifyingKey::from_bytes(
92                    id.as_slice()
93                        .try_into()
94                        .map_err(|_| IdError::WrongIdLength(id.len()))?,
95                )
96                .map_err(|e| IdError::InvalidPublicKey(e.to_string().into()))?;
97
98                let sig_bytes = crev_common::base64_decode(sig_str)
99                    .map_err(|e| IdError::InvalidSignature(e.to_string().into()))?;
100                let signature = ed25519_dalek::Signature::try_from(sig_bytes.as_slice())
101                    .map_err(|e| IdError::InvalidSignature(e.to_string().into()))?;
102                pubkey
103                    .verify(content, &signature)
104                    .map_err(|e| IdError::InvalidSignature(e.to_string().into()))?;
105            }
106        }
107
108        Ok(())
109    }
110
111    #[must_use]
112    pub fn to_bytes(&self) -> Vec<u8> {
113        match self {
114            Id::Crev { id } => id.clone(),
115        }
116    }
117}
118
119/// A unique ID accompanied by publicly identifying data.
120#[derive(Clone, Debug, Builder, Serialize, Deserialize, PartialEq, Eq, Hash)]
121pub struct PublicId {
122    #[serde(flatten)]
123    pub id: Id,
124    #[serde(flatten)]
125    pub url: Option<Url>,
126}
127
128impl PublicId {
129    #[must_use]
130    pub fn new(id: Id, url: Url) -> Self {
131        Self { id, url: Some(url) }
132    }
133
134    #[must_use]
135    pub fn new_id_only(id: Id) -> Self {
136        Self { id, url: None }
137    }
138
139    pub fn new_from_pubkey(v: Vec<u8>, url: Option<Url>) -> Result<Self, IdError> {
140        Ok(Self {
141            id: Id::new_crev(v)?,
142            url,
143        })
144    }
145
146    pub fn new_crevid_from_base64(s: &str, url: Url) -> Result<Self, IdError> {
147        let v = crev_common::base64_decode(s)
148            .map_err(|e| IdError::InvalidCrevId(e.to_string().into()))?;
149        Ok(Self {
150            id: Id::new_crev(v)?,
151            url: Some(url),
152        })
153    }
154
155    pub fn create_trust_proof<'a>(
156        &self,
157        ids: impl IntoIterator<Item = &'a PublicId>,
158        trust_level: proof::trust::TrustLevel,
159        override_: Vec<OverrideItem>,
160    ) -> crate::Result<proof::Trust> {
161        proof::TrustBuilder::default()
162            .from(self.clone())
163            .trust(trust_level)
164            .ids(ids.into_iter().cloned().collect())
165            .override_(override_)
166            .build()
167            .map_err(|e| crate::Error::BuildingProof(e.to_string().into()))
168    }
169
170    pub fn create_package_review_proof(
171        &self,
172        package: proof::PackageInfo,
173        review: proof::review::Review,
174        override_: Vec<OverrideItem>,
175        comment: String,
176    ) -> crate::Result<proof::review::Package> {
177        proof::review::PackageBuilder::default()
178            .from(self.clone())
179            .package(package)
180            .review(review)
181            .override_(override_)
182            .comment(comment)
183            .build()
184            .map_err(|e| crate::Error::BuildingProof(e.to_string().into()))
185    }
186
187    #[must_use]
188    pub fn url_display(&self) -> &str {
189        match &self.url {
190            Some(url) => &url.url,
191            None => "(no url)",
192        }
193    }
194}
195
196/// A `PublicId` with the corresponding secret key
197#[derive(Debug)]
198pub struct UnlockedId {
199    pub id: PublicId,
200    pub keypair: Keypair,
201}
202
203#[derive(Debug)]
204pub struct Keypair {
205    pub secret: SigningKey,
206    pub public: VerifyingKey,
207}
208
209impl AsRef<Id> for UnlockedId {
210    fn as_ref(&self) -> &Id {
211        &self.id.id
212    }
213}
214
215impl AsRef<PublicId> for UnlockedId {
216    fn as_ref(&self) -> &PublicId {
217        &self.id
218    }
219}
220
221impl UnlockedId {
222    #[allow(clippy::new_ret_no_self)]
223    pub fn new(url: Option<Url>, sec_key: &[u8]) -> Result<Self, IdError> {
224        let sec_key = SigningKey::from_bytes(
225            sec_key
226                .try_into()
227                .map_err(|_| IdError::WrongIdLength(sec_key.len()))?,
228        );
229        let calculated_pub_key = sec_key.verifying_key();
230
231        Ok(Self {
232            id: crate::PublicId::new_from_pubkey(calculated_pub_key.as_bytes().to_vec(), url)?,
233            keypair: Keypair {
234                secret: sec_key,
235                public: calculated_pub_key,
236            },
237        })
238    }
239
240    #[must_use]
241    pub fn sign(&self, msg: &[u8]) -> Vec<u8> {
242        self.keypair.secret.sign(msg).to_bytes().to_vec()
243    }
244
245    #[must_use]
246    pub fn type_as_string(&self) -> String {
247        "crev".into()
248    }
249
250    #[must_use]
251    pub fn as_public_id(&self) -> &PublicId {
252        &self.id
253    }
254
255    #[must_use]
256    pub fn url(&self) -> Option<&Url> {
257        self.id.url.as_ref()
258    }
259
260    #[must_use]
261    pub fn generate_for_git_url(url: &str) -> Self {
262        Self::generate(Some(Url::new_git(url.to_owned())))
263    }
264
265    pub fn generate(url: Option<Url>) -> Self {
266        let secret = ed25519_dalek::SigningKey::generate(&mut OsRng);
267        let public = secret.verifying_key();
268        Self {
269            id: PublicId::new_from_pubkey(public.as_bytes().to_vec(), url)
270                .expect("should be valid keypair"),
271            keypair: Keypair { secret, public },
272        }
273    }
274
275    pub fn create_signed_trust_proof<'a>(
276        &self,
277        ids: impl IntoIterator<Item = &'a PublicId>,
278        trust_level: proof::trust::TrustLevel,
279        override_: Vec<OverrideItem>,
280    ) -> crate::Result<proof::Proof> {
281        self.id
282            .create_trust_proof(ids, trust_level, override_)?
283            .sign_by(self)
284    }
285}