1use std::ops::Add;
2use bytes::Bytes;
3use chrono::DateTime;
4use chrono::Duration;
5use chrono::FixedOffset;
6use chrono::SecondsFormat;
7use chrono::Utc;
8use cid::Cid;
9use libp2p_identity::Keypair;
10use libp2p_identity::PeerId;
11use libp2p_identity::PublicKey;
12use quick_protobuf::MessageWrite;
13use quick_protobuf::Writer;
14use quick_protobuf::{BytesReader, MessageRead};
15use serde::{Deserialize, Serialize, Serializer};
16
17mod generate;
18
19#[derive(
20 Clone,
21 Copy,
22 Debug,
23 PartialEq,
24 Eq,
25 Hash,
26 PartialOrd,
27 Ord,
28 derive_more::Display,
29)]
30#[repr(i32)]
31pub enum ValidityType {
32 EOL = 0,
33}
34
35impl Serialize for ValidityType {
36 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
37 where
38 S: Serializer,
39 {
40 serializer.serialize_i32(*self as i32)
41 }
42}
43
44impl<'de> Deserialize<'de> for ValidityType {
45 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
46 where
47 D: serde::Deserializer<'de>,
48 {
49 let i = i32::deserialize(deserializer)?;
50 ValidityType::try_from(i).map_err(serde::de::Error::custom)
51 }
52}
53
54impl TryFrom<i32> for ValidityType {
55 type Error = std::io::Error;
56 fn try_from(i: i32) -> Result<Self, Self::Error> {
57 match i {
58 0 => Ok(ValidityType::EOL),
59 _ => Err(std::io::Error::new(
60 std::io::ErrorKind::InvalidData,
61 "invalid validity type",
62 )),
63 }
64 }
65}
66
67impl From<ValidityType> for i32 {
68 fn from(ty: ValidityType) -> Self {
69 ty as i32
70 }
71}
72
73impl From<generate::ipns_pb::mod_IpnsEntry::ValidityType> for ValidityType {
74 fn from(v_ty: generate::ipns_pb::mod_IpnsEntry::ValidityType) -> Self {
75 match v_ty {
76 generate::ipns_pb::mod_IpnsEntry::ValidityType::EOL => ValidityType::EOL,
77 }
78 }
79}
80
81#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
82#[repr(i32)]
83pub enum KeyType {
84 RSA = 0,
85 Ed25519 = 1,
86 Secp256k1 = 2,
87 ECDSA = 3,
88}
89
90#[cfg(feature = "libp2p")]
91impl From<libp2p_identity::KeyType> for KeyType {
92 fn from(ty: libp2p_identity::KeyType) -> Self {
93 match ty {
94 libp2p_identity::KeyType::Ed25519 => KeyType::Ed25519,
95 libp2p_identity::KeyType::RSA => KeyType::RSA,
96 libp2p_identity::KeyType::Secp256k1 => KeyType::Secp256k1,
97 libp2p_identity::KeyType::Ecdsa => KeyType::ECDSA,
98 }
99 }
100}
101
102#[derive(Clone, Debug)]
103pub struct Record {
104 data: Vec<u8>,
105
106 value: Vec<u8>,
107 validity_type: ValidityType,
108 validity: Vec<u8>,
109 sequence: u64,
110 ttl: u64,
111
112 public_key: Vec<u8>,
113
114 signature_v1: Vec<u8>,
115 signature_v2: Vec<u8>,
116}
117
118impl From<generate::ipns_pb::IpnsEntry<'_>> for Record {
119 fn from(entry: generate::ipns_pb::IpnsEntry<'_>) -> Self {
120 Record {
121 data: entry.data.into(),
122 value: entry.value.into(),
123 validity_type: entry.validityType.into(),
124 validity: entry.validity.into(),
125 sequence: entry.sequence,
126 ttl: entry.ttl,
127 public_key: entry.pubKey.into(),
128 signature_v1: entry.signatureV1.into(),
129 signature_v2: entry.signatureV2.into(),
130 }
131 }
132}
133
134impl<'a> From<&'a Record> for generate::ipns_pb::IpnsEntry<'a> {
135 fn from(record: &'a Record) -> Self {
136 generate::ipns_pb::IpnsEntry {
137 validity: (&record.validity).into(),
138 validityType: generate::ipns_pb::mod_IpnsEntry::ValidityType::EOL,
139 value: (&record.value).into(),
140 signatureV1: (&record.signature_v1).into(),
141 signatureV2: (&record.signature_v2).into(),
142 sequence: record.sequence,
143 pubKey: (&record.public_key).into(),
144 ttl: record.ttl,
145 data: (&record.data).into(),
146 }
147 }
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
153pub struct Data {
154 #[serde(rename = "Value")]
155 pub value: Bytes,
156
157 #[serde(rename = "ValidityType")]
158 pub validity_type: ValidityType,
159
160 #[serde(rename = "Validity")]
161 pub validity: Bytes,
162
163 #[serde(rename = "Sequence")]
164 pub sequence: u64,
165
166 #[serde(rename = "TTL")]
167 pub ttl: u64,
168}
169
170impl Data {
171 pub fn value(&self) -> &[u8] {
172 &self.value
173 }
174
175 pub fn validity_type(&self) -> ValidityType {
176 self.validity_type
177 }
178
179 pub fn validity(&self) -> &[u8] {
180 &self.validity
181 }
182
183 pub fn sequence(&self) -> u64 {
184 self.sequence
185 }
186
187 pub fn ttl(&self) -> u64 {
188 self.ttl
189 }
190}
191
192impl Record {
193 #[cfg(feature = "libp2p")]
194 pub fn new(
195 keypair: &Keypair,
196 value: impl AsRef<[u8]>,
197 duration: Duration,
198 seq: u64,
199 ttl: u64,
200 ) -> std::io::Result<Self> {
201 let value = value.as_ref().to_vec();
202
203 let validity = Utc::now()
204 .add(duration)
205 .to_rfc3339_opts(SecondsFormat::Nanos, true)
206 .into_bytes();
207
208 let validity_type = ValidityType::EOL;
209
210 let signature_v1_construct = {
211 let mut data = Vec::with_capacity(value.len() + validity.len() + 3);
212
213 data.extend(value.iter());
214 data.extend(validity.iter());
215 data.extend(validity_type.to_string().as_bytes());
216
217 data
218 };
219
220 let signature_v1 = keypair
221 .sign(&signature_v1_construct)
222 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
223
224 let document = Data {
225 value: Bytes::from(value.clone()),
226 validity_type,
227 validity: Bytes::from(validity.clone()),
228 sequence: seq,
229 ttl,
230 };
231
232 let data = serde_ipld_dagcbor::to_vec(&document)
233 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
234
235 let mut signature_v2_construct = vec![
236 0x69, 0x70, 0x6e, 0x73, 0x2d, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65,
237 0x3a,
238 ];
239
240 signature_v2_construct.extend(data.iter());
241
242 let signature_v2 = keypair
243 .sign(&signature_v2_construct)
244 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
245
246 let public_key = match keypair.key_type().into() {
247 KeyType::RSA => keypair
248 .to_protobuf_encoding()
249 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?,
250 _ => vec![],
251 };
252
253 Ok(Record {
254 data,
255 value,
256 validity_type,
257 validity,
258 sequence: seq,
259 ttl,
260 public_key,
261 signature_v1,
262 signature_v2,
263 })
264 }
265
266 pub fn decode(data: impl AsRef<[u8]>) -> std::io::Result<Self> {
267 let data = data.as_ref();
268
269 if data.len() > 10 * 1024 {
270 return Err(std::io::Error::from(std::io::ErrorKind::InvalidData));
271 }
272
273 let mut reader = BytesReader::from_bytes(data);
274 let entry = generate::ipns_pb::IpnsEntry::from_reader(&mut reader, data)
275 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
276 let record = entry.into();
277 Ok(record)
278 }
279
280 pub fn encode(&self) -> std::io::Result<Vec<u8>> {
281 let entry: generate::ipns_pb::IpnsEntry = self.into();
282
283 let mut buf = Vec::with_capacity(entry.get_size());
284 let mut writer = Writer::new(&mut buf);
285
286 entry
287 .write_message(&mut writer)
288 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
289
290 Ok(buf)
291 }
292}
293
294impl Record {
295 pub fn sequence(&self) -> u64 {
296 self.sequence
297 }
298
299 pub fn validity_type(&self) -> ValidityType {
300 self.validity_type
301 }
302
303 pub fn validity(&self) -> std::io::Result<DateTime<FixedOffset>> {
304 let time = String::from_utf8_lossy(&self.validity);
305 chrono::DateTime::parse_from_rfc3339(&time)
306 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
307 }
308
309 pub fn ttl(&self) -> u64 {
310 self.ttl
311 }
312
313 pub fn signature_v1(&self) -> bool {
314 !self.signature_v1.is_empty()
315 }
316
317 pub fn signature_v2(&self) -> bool {
318 !self.signature_v2.is_empty()
319 }
320
321 pub fn data(&self) -> std::io::Result<Data> {
322 let data: Data = serde_ipld_dagcbor::from_slice(&self.data)
323 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
324
325 if data.value != self.value
326 || data.validity != self.validity
327 || data.validity_type != self.validity_type
328 || data.sequence != self.sequence
329 || data.ttl != self.ttl
330 {
331 return Err(std::io::Error::from(std::io::ErrorKind::InvalidData));
332 }
333
334 Ok(data)
335 }
336
337 pub fn value(&self) -> std::io::Result<Cid> {
338 let cid_str = String::from_utf8_lossy(&self.value);
339 Cid::try_from(cid_str.as_ref())
340 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
341 }
342
343 #[cfg(feature = "libp2p")]
344 pub fn verify(&self, peer_id: PeerId) -> std::io::Result<()> {
345 use multihash::Multihash;
346
347 if self.signature_v2.is_empty() && self.signature_v1.is_empty() {
348 return Err(std::io::Error::new(
349 std::io::ErrorKind::Other,
350 "Empty signature field",
351 ));
352 }
353
354 if self.data.is_empty() {
355 return Err(std::io::Error::new(
356 std::io::ErrorKind::InvalidData,
357 "Empty data field",
358 ));
359 }
360
361 let key = peer_id.to_bytes();
362
363 let mh = Multihash::from_bytes(&key).expect("valid hash");
364 let cid = Cid::new_v1(0x72, mh);
365
366 let public_key = match self.public_key.is_empty() {
367 true => cid.hash().digest(),
368 false => self.public_key.as_ref(),
370 };
371
372 let pk = PublicKey::try_decode_protobuf(public_key)
373 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
374
375 if matches!(pk.key_type().into(), KeyType::RSA) {
377 return Err(std::io::Error::new(
378 std::io::ErrorKind::Unsupported,
379 "RSA Keys are not supported at this time",
380 ));
381 }
382
383 let mut signature_v2 = vec![
384 0x69, 0x70, 0x6e, 0x73, 0x2d, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65,
385 0x3a,
386 ];
387
388 signature_v2.extend(self.data.iter());
389
390 self.data()?;
391
392 if !pk.verify(&signature_v2, &self.signature_v2) {
393 return Err(std::io::Error::new(
394 std::io::ErrorKind::InvalidData,
395 "Signature is invalid",
396 ));
397 }
398
399 Ok(())
400 }
401}