crev_data/
id.rs

1use crate::{
2    proof::{self, ContentExt, OverrideItem},
3    Url,
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(id.as_slice().try_into().map_err(|_| IdError::WrongIdLength(id.len()))?)
92                    .map_err(|e| IdError::InvalidPublicKey(e.to_string().into()))?;
93
94                let sig_bytes = crev_common::base64_decode(sig_str)
95                    .map_err(|e| IdError::InvalidSignature(e.to_string().into()))?;
96                let signature = ed25519_dalek::Signature::try_from(sig_bytes.as_slice())
97                    .map_err(|e| IdError::InvalidSignature(e.to_string().into()))?;
98                pubkey
99                    .verify(content, &signature)
100                    .map_err(|e| IdError::InvalidSignature(e.to_string().into()))?;
101            }
102        }
103
104        Ok(())
105    }
106
107    #[must_use]
108    pub fn to_bytes(&self) -> Vec<u8> {
109        match self {
110            Id::Crev { id } => id.clone(),
111        }
112    }
113}
114
115/// A unique ID accompanied by publicly identifying data.
116#[derive(Clone, Debug, Builder, Serialize, Deserialize, PartialEq, Eq, Hash)]
117pub struct PublicId {
118    #[serde(flatten)]
119    pub id: Id,
120    #[serde(flatten)]
121    pub url: Option<Url>,
122}
123
124impl PublicId {
125    #[must_use]
126    pub fn new(id: Id, url: Url) -> Self {
127        Self { id, url: Some(url) }
128    }
129
130    #[must_use]
131    pub fn new_id_only(id: Id) -> Self {
132        Self { id, url: None }
133    }
134
135    pub fn new_from_pubkey(v: Vec<u8>, url: Option<Url>) -> Result<Self, IdError> {
136        Ok(Self {
137            id: Id::new_crev(v)?,
138            url,
139        })
140    }
141
142    pub fn new_crevid_from_base64(s: &str, url: Url) -> Result<Self, IdError> {
143        let v = crev_common::base64_decode(s)
144            .map_err(|e| IdError::InvalidCrevId(e.to_string().into()))?;
145        Ok(Self {
146            id: Id::new_crev(v)?,
147            url: Some(url),
148        })
149    }
150
151    pub fn create_trust_proof<'a>(
152        &self,
153        ids: impl IntoIterator<Item = &'a PublicId>,
154        trust_level: proof::trust::TrustLevel,
155        override_: Vec<OverrideItem>,
156    ) -> crate::Result<proof::Trust> {
157        proof::TrustBuilder::default()
158            .from(self.clone())
159            .trust(trust_level)
160            .ids(ids.into_iter().cloned().collect())
161            .override_(override_)
162            .build()
163            .map_err(|e| crate::Error::BuildingProof(e.to_string().into()))
164    }
165
166    pub fn create_package_review_proof(
167        &self,
168        package: proof::PackageInfo,
169        review: proof::review::Review,
170        override_: Vec<OverrideItem>,
171        comment: String,
172    ) -> crate::Result<proof::review::Package> {
173        proof::review::PackageBuilder::default()
174            .from(self.clone())
175            .package(package)
176            .review(review)
177            .override_(override_)
178            .comment(comment)
179            .build()
180            .map_err(|e| crate::Error::BuildingProof(e.to_string().into()))
181    }
182
183    #[must_use]
184    pub fn url_display(&self) -> &str {
185        match &self.url {
186            Some(url) => &url.url,
187            None => "(no url)",
188        }
189    }
190}
191
192/// A `PublicId` with the corresponding secret key
193#[derive(Debug)]
194pub struct UnlockedId {
195    pub id: PublicId,
196    pub keypair: Keypair,
197}
198
199#[derive(Debug)]
200pub struct Keypair {
201    pub secret: SigningKey,
202    pub public: VerifyingKey,
203}
204
205impl AsRef<Id> for UnlockedId {
206    fn as_ref(&self) -> &Id {
207        &self.id.id
208    }
209}
210
211impl AsRef<PublicId> for UnlockedId {
212    fn as_ref(&self) -> &PublicId {
213        &self.id
214    }
215}
216
217impl UnlockedId {
218    #[allow(clippy::new_ret_no_self)]
219    pub fn new(url: Option<Url>, sec_key: &[u8]) -> Result<Self, IdError> {
220        let sec_key = SigningKey::from_bytes(sec_key.try_into().map_err(|_| IdError::WrongIdLength(sec_key.len()))?);
221        let calculated_pub_key = sec_key.verifying_key();
222
223        Ok(Self {
224            id: crate::PublicId::new_from_pubkey(calculated_pub_key.as_bytes().to_vec(), url)?,
225            keypair: Keypair {
226                secret: sec_key,
227                public: calculated_pub_key,
228            },
229        })
230    }
231
232    #[must_use]
233    pub fn sign(&self, msg: &[u8]) -> Vec<u8> {
234        self.keypair.secret.sign(msg).to_bytes().to_vec()
235    }
236
237    #[must_use]
238    pub fn type_as_string(&self) -> String {
239        "crev".into()
240    }
241
242    #[must_use]
243    pub fn as_public_id(&self) -> &PublicId {
244        &self.id
245    }
246
247    #[must_use]
248    pub fn url(&self) -> Option<&Url> {
249        self.id.url.as_ref()
250    }
251
252    #[must_use]
253    pub fn generate_for_git_url(url: &str) -> Self {
254        Self::generate(Some(Url::new_git(url.to_owned())))
255    }
256
257    pub fn generate(url: Option<Url>) -> Self {
258        let secret = ed25519_dalek::SigningKey::generate(&mut OsRng);
259        let public = secret.verifying_key();
260        Self {
261            id: PublicId::new_from_pubkey(public.as_bytes().to_vec(), url)
262                .expect("should be valid keypair"),
263            keypair: Keypair { secret, public },
264        }
265    }
266
267    pub fn create_signed_trust_proof<'a>(
268        &self,
269        ids: impl IntoIterator<Item = &'a PublicId>,
270        trust_level: proof::trust::TrustLevel,
271        override_: Vec<OverrideItem>,
272    ) -> crate::Result<proof::Proof> {
273        self.id
274            .create_trust_proof(ids, trust_level, override_)?
275            .sign_by(self)
276    }
277}