rust_ipns/
lib.rs

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            //TODO: Validate internal public key against the multhash publickey
339            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        //TODO: Implement support for RSA
346        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}