zerotier_api/
identity.rs

1use crate::{Address, InternalError, SecretKey, VerifyingKey};
2
3use ed25519_dalek::SigningKey;
4use failure::*;
5
6use std::convert::{TryFrom, TryInto};
7use std::fs;
8use std::path::Path;
9
10/// Combination of [`Address`](struct.Address.html), [`VerifyingKey`](struct.VerifyingKey) and optionally
11/// [`SecretKey`](struct.SecretKey.html).
12pub struct Identity {
13    pub address: Address,
14    pub public_key: VerifyingKey,
15    pub secret_key: Option<SecretKey>,
16}
17
18impl Identity {
19    /// Read ZeroTier identity from given location.
20    pub fn read<P: AsRef<Path>>(path: P) -> Fallible<Self> {
21        Identity::try_from(&fs::read_to_string(path)?[..])
22    }
23
24    /// Read ZeroTier identity from default location.
25    pub fn read_default() -> Fallible<Self> {
26        Identity::read("/var/lib/zerotier-one/identity.secret")
27    }
28}
29
30impl TryFrom<SecretKey> for Identity {
31    type Error = Error;
32
33    fn try_from(secret_key: SecretKey) -> Fallible<Self> {
34        let public_key = VerifyingKey::from(&secret_key);
35
36        Ok(Self {
37            address: Address::try_from(&public_key)?,
38            public_key: VerifyingKey::from(&secret_key),
39            secret_key: Some(secret_key),
40        })
41    }
42}
43
44/// TODO: use IO reader instead
45impl TryFrom<&str> for Identity {
46    type Error = Error;
47
48    fn try_from(identity: &str) -> Fallible<Self> {
49        let split_identity: Vec<&str> = identity.split(':').collect();
50        let (address1, public_key1, maybe_secret_key1) = match &split_identity[..] {
51            [address, "0", public_key] => (address, public_key, None),
52            [address, "0", public_key, secret_key] => (address, public_key, Some(secret_key)),
53            _ => return Err(InternalError::MalformedIdentity.into()),
54        };
55        let address = Address::try_from(hex::decode(address1)?.as_slice())?;
56        let public_key = VerifyingKey::try_from(hex::decode(public_key1)?.as_slice())?;
57        let secret_key = match maybe_secret_key1 {
58            Some(secret_key) => Some(SecretKey::try_from(hex::decode(secret_key)?.as_slice())?),
59            None => None,
60        };
61        Ok(Identity {
62            address,
63            public_key,
64            secret_key,
65        })
66    }
67}
68
69impl TryInto<SigningKey> for Identity {
70    type Error = Error;
71
72    fn try_into(self) -> Fallible<SigningKey> {
73        Ok(SigningKey::from_bytes(&self.secret_key.unwrap().ed))
74    }
75}
76
77#[cfg(test)]
78pub mod tests {
79    use ed25519_dalek::{Signer, Verifier};
80
81    use super::*;
82
83    #[test]
84    fn test_identity() -> Fallible<()> {
85        // nix-shell -p zerotierone --run 'zerotier-idtool generate'
86        let identity_str = "538c34e03c:0:070288330a72d2aa3cb7935dfe6028d9fb83bdb42240aaa05e33529121babd183ff775351742a47487454195c08c0e83c520e7466fcdde3396a0c4cd40557737:f20542ab6955fe140fb3a5be9557666b9c89a3e2b73432de46d827d11736773aca15c3e03b89a1d09436ae45bc02f84b8d5a0a2f6c0d42b3856c2b22f5ab2b27";
87        let identity = Identity::try_from(identity_str)?;
88
89        // assert_eq!(identity.address, Address::try_from(&identity.public_key)?);
90
91        let secret_key = identity.secret_key.unwrap();
92        let public_key = identity.public_key.clone();
93
94        assert_eq!(identity.public_key.ed, public_key.ed);
95        assert_eq!(identity.public_key.dh.as_bytes(), public_key.dh.as_bytes());
96
97        let keypair = ed25519_dalek::SigningKey::from_bytes(&secret_key.ed);
98
99        let message = b"7VbLpreCRY738Sw4OGecCw";
100        let signature = keypair.sign(message);
101
102        identity.public_key.ed.verify(message, &signature)?;
103
104        Ok(())
105    }
106}