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#[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#[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#[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}