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