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