1use bytes::Bytes;
2use chrono::DateTime;
3use chrono::Duration;
4use chrono::FixedOffset;
5use chrono::SecondsFormat;
6use chrono::Utc;
7use cid::Cid;
8use libp2p_identity::Keypair;
9use libp2p_identity::PeerId;
10use libp2p_identity::PublicKey;
11use quick_protobuf::MessageWrite;
12use quick_protobuf::Writer;
13use quick_protobuf::{BytesReader, MessageRead};
14use serde::{Deserialize, Serialize, Serializer};
15use std::ops::Add;
16
17mod generate;
18
19const SIGNATURE_V2_BASE: &[u8] = &[
20 0x69, 0x70, 0x6e, 0x73, 0x2d, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x3a,
21];
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
24#[repr(i32)]
25pub enum ValidityType {
26 EOL = 0,
27}
28
29impl std::fmt::Display for ValidityType {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 write!(f, "EOL")
32 }
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 signature_v2_construct = SIGNATURE_V2_BASE
236 .iter()
237 .chain(data.iter())
238 .copied()
239 .collect::<Vec<_>>();
240
241 let signature_v2 = keypair
242 .sign(&signature_v2_construct)
243 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
244
245 let public_key = match keypair.key_type().into() {
246 KeyType::RSA => keypair
247 .to_protobuf_encoding()
248 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?,
249 _ => vec![],
250 };
251
252 Ok(Record {
253 data,
254 value,
255 validity_type,
256 validity,
257 sequence: seq,
258 ttl,
259 public_key,
260 signature_v1,
261 signature_v2,
262 })
263 }
264
265 pub fn decode(data: impl AsRef<[u8]>) -> std::io::Result<Self> {
266 let data = data.as_ref();
267
268 if data.len() > 10 * 1024 {
269 return Err(std::io::Error::from(std::io::ErrorKind::InvalidData));
270 }
271
272 let mut reader = BytesReader::from_bytes(data);
273 let entry = generate::ipns_pb::IpnsEntry::from_reader(&mut reader, data)
274 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
275 let record = entry.into();
276 Ok(record)
277 }
278
279 pub fn encode(&self) -> std::io::Result<Vec<u8>> {
280 let entry: generate::ipns_pb::IpnsEntry = self.into();
281
282 let mut buf = Vec::with_capacity(entry.get_size());
283 let mut writer = Writer::new(&mut buf);
284
285 entry
286 .write_message(&mut writer)
287 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
288
289 Ok(buf)
290 }
291}
292
293impl Record {
294 pub fn sequence(&self) -> u64 {
295 self.sequence
296 }
297
298 pub fn validity_type(&self) -> ValidityType {
299 self.validity_type
300 }
301
302 pub fn validity(&self) -> std::io::Result<DateTime<FixedOffset>> {
303 let time = String::from_utf8_lossy(&self.validity);
304 chrono::DateTime::parse_from_rfc3339(&time)
305 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
306 }
307
308 pub fn ttl(&self) -> u64 {
309 self.ttl
310 }
311
312 pub fn signature_v1(&self) -> bool {
313 !self.signature_v1.is_empty()
314 }
315
316 pub fn signature_v2(&self) -> bool {
317 !self.signature_v2.is_empty()
318 }
319
320 pub fn data(&self) -> std::io::Result<Data> {
321 let data: Data = serde_ipld_dagcbor::from_slice(&self.data)
322 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
323
324 if data.value != self.value
325 || data.validity != self.validity
326 || data.validity_type != self.validity_type
327 || data.sequence != self.sequence
328 || data.ttl != self.ttl
329 {
330 return Err(std::io::Error::from(std::io::ErrorKind::InvalidData));
331 }
332
333 Ok(data)
334 }
335
336 pub fn value(&self) -> std::io::Result<Cid> {
337 let cid_str = String::from_utf8_lossy(&self.value);
338 Cid::try_from(cid_str.as_ref())
339 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))
340 }
341
342 #[cfg(feature = "libp2p")]
343 pub fn verify(&self, peer_id: PeerId) -> std::io::Result<()> {
344 use multihash::Multihash;
345
346 if self.signature_v2.is_empty() && self.signature_v1.is_empty() {
347 return Err(std::io::Error::new(
348 std::io::ErrorKind::Other,
349 "Empty signature field",
350 ));
351 }
352
353 if self.data.is_empty() {
354 return Err(std::io::Error::new(
355 std::io::ErrorKind::InvalidData,
356 "Empty data field",
357 ));
358 }
359
360 let key = peer_id.to_bytes();
361
362 let mh = Multihash::from_bytes(&key).expect("valid hash");
363 let cid = Cid::new_v1(0x72, mh);
364
365 let public_key = match self.public_key.is_empty() {
366 true => cid.hash().digest(),
367 false => self.public_key.as_ref(),
369 };
370
371 let pk = PublicKey::try_decode_protobuf(public_key)
372 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
373
374 if matches!(pk.key_type().into(), KeyType::RSA) {
376 return Err(std::io::Error::new(
377 std::io::ErrorKind::Unsupported,
378 "RSA Keys are not supported at this time",
379 ));
380 }
381
382 let signature_v2 = SIGNATURE_V2_BASE
383 .iter()
384 .chain(self.data.iter())
385 .copied()
386 .collect::<Vec<_>>();
387
388 self.data()?;
389
390 if !pk.verify(&signature_v2, &self.signature_v2) {
391 return Err(std::io::Error::new(
392 std::io::ErrorKind::InvalidData,
393 "Signature is invalid",
394 ));
395 }
396
397 Ok(())
398 }
399}