API about wrapped.

This commit is contained in:
Adam Ierymenko 2020-12-17 18:37:25 -05:00
parent ab2870cbb7
commit b1777f09c8
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
11 changed files with 547 additions and 101 deletions

2
.gitignore vendored
View file

@ -38,3 +38,5 @@ node_modules
/*.db /*.db
/*.opendb /*.opendb
/rust-zerotier-core/src/bindings/capi.rs /rust-zerotier-core/src/bindings/capi.rs
/rust-zerotier-core/target
/rust-zerotier-service/target

View file

@ -6,6 +6,18 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "hex"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
[[package]]
name = "itoa"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
[[package]] [[package]]
name = "num-derive" name = "num-derive"
version = "0.3.3" version = "0.3.3"
@ -48,8 +60,48 @@ dependencies = [
name = "rust-zerotier-core" name = "rust-zerotier-core"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"hex",
"num-derive", "num-derive",
"num-traits", "num-traits",
"serde",
"serde_json",
]
[[package]]
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "serde"
version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779"
dependencies = [
"itoa",
"ryu",
"serde",
] ]
[[package]] [[package]]

View file

@ -9,3 +9,6 @@ edition = "2018"
[dependencies] [dependencies]
num-traits = "0.2.14" num-traits = "0.2.14"
num-derive = "0.3.3" num-derive = "0.3.3"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
hex = "0.4.2"

View file

@ -1,9 +1,41 @@
pub type Address = u64; pub struct Address(pub u64);
pub fn address_to_string(a: Address) -> String { impl Address {
format!("{:0>10x}", a as u64) #[inline]
pub fn new_from_string(s: &str) -> Address {
return Address(u64::from_str_radix(s, 16).unwrap_or(0));
}
} }
pub fn address_from_string(s: &str) -> Address { impl ToString for Address {
return u64::from_str_radix(s, 16).unwrap_or(0) as Address; #[inline]
fn to_string(&self) -> String {
format!("{:0>10x}", self.0)
}
}
impl serde::Serialize for Address {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
serializer.serialize_str(self.to_string().as_str())
}
}
struct AddressVisitor;
impl<'de> serde::de::Visitor<'de> for AddressVisitor {
type Value = Address;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("ZeroTier Address in string format")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error {
Ok(Address::new_from_string(s))
}
}
impl<'de> serde::Deserialize<'de> for Address {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
deserializer.deserialize_str(AddressVisitor)
}
} }

View file

@ -2,55 +2,173 @@ use crate::*;
use crate::bindings::capi as ztcore; use crate::bindings::capi as ztcore;
use std::ffi::CStr; use std::ffi::CStr;
use std::ptr::copy_nonoverlapping; use std::ptr::copy_nonoverlapping;
use std::os::raw::c_char; use std::os::raw::{c_char, c_void};
use serde::{Deserialize, Serialize};
/// Maximum length of a string in a certificate (mostly for the certificate name fields).
pub const CERTIFICATE_MAX_STRING_LENGTH: u32 = ztcore::ZT_CERTIFICATE_MAX_STRING_LENGTH;
/// Certificate local trust bit field flag: this certificate self-signs a root CA.
pub const CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA: u32 = ztcore::ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA;
/// Certificate local trust bit field flag: this certificate specifies a set of ZeroTier roots.
pub const CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET: u32 = ztcore::ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET;
/// Length of a NIST P-384 unique ID (public key).
pub const CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE: u32 = ztcore::ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE;
/// Length of a private key corresponding to a NIST P-384 unique ID.
pub const CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE: u32 = ztcore::ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE;
/// Type of certificate subject unique ID
#[derive(FromPrimitive,ToPrimitive)]
pub enum CertificateUniqueIdType {
NistP384 = ztcore::ZT_CertificateUniqueIdType_ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384 as isize
}
impl CertificateUniqueIdType {
pub fn new_from_string(s: &str) -> Result<CertificateUniqueIdType, ResultCode> {
if s.to_ascii_lowercase() == "nistp384" {
return Ok(CertificateUniqueIdType::NistP384);
}
return Err(ResultCode::ErrorBadParameter);
}
}
impl ToString for CertificateUniqueIdType {
fn to_string(&self) -> String {
match *self {
CertificateUniqueIdType::NistP384 => String::from("NistP384")
}
}
}
impl serde::Serialize for CertificateUniqueIdType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
serializer.serialize_str(self.to_string().as_str())
}
}
struct CertificateUniqueIdTypeVisitor;
impl<'de> serde::de::Visitor<'de> for CertificateUniqueIdTypeVisitor {
type Value = CertificateUniqueIdType;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("CertificateUniqueIdType value in string form")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error {
let id = CertificateUniqueIdType::new_from_string(s);
if id.is_err() {
return Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self));
}
return Ok(id.ok().unwrap() as Self::Value);
}
}
impl<'de> serde::Deserialize<'de> for CertificateUniqueIdType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
deserializer.deserialize_str(CertificateUniqueIdTypeVisitor)
}
}
/// Reasons a certificate may be rejected.
#[derive(FromPrimitive,ToPrimitive)]
pub enum CertificateError {
None = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_NONE as isize,
HaveNewerCert = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_HAVE_NEWER_CERT as isize,
InvalidFormat = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_INVALID_FORMAT as isize,
InvalidIdentity = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_INVALID_IDENTITY as isize,
InvalidPrimarySignature = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE as isize,
InvalidChain = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_INVALID_CHAIN as isize,
InvalidComponentSignature = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE as isize,
InvalidUniqueIdProof = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF as isize,
MissingRequiredFields = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS as isize,
OutOfValidTimeWindow = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_OUT_OF_VALID_TIME_WINDOW as isize,
}
impl ToString for CertificateError {
fn to_string(&self) -> String {
String::from(
match self {
CertificateError::None => "None",
CertificateError::HaveNewerCert => "HaveNewerCert",
CertificateError::InvalidFormat => "InvalidFormat",
CertificateError::InvalidIdentity => "InavlidIdentity",
CertificateError::InvalidPrimarySignature => "InvalidPrimarySignature",
CertificateError::InvalidChain => "InvalidChain",
CertificateError::InvalidComponentSignature => "InvalidComponentSignature",
CertificateError::InvalidUniqueIdProof => "InvalidUniqueIdProof",
CertificateError::MissingRequiredFields => "MissingRequiredFields",
CertificateError::OutOfValidTimeWindow => "OutOfValidTimeWindow"
}
)
}
}
#[allow(non_snake_case)]
#[derive(Serialize, Deserialize)]
pub struct CertificateName { pub struct CertificateName {
pub serial_no: String, pub serialNo: String,
pub common_name: String, pub commonName: String,
pub country: String, pub country: String,
pub organization: String, pub organization: String,
pub unit: String, pub unit: String,
pub locality: String, pub locality: String,
pub province: String, pub province: String,
pub street_address: String, pub streetAddress: String,
pub postal_code: String, pub postalCode: String,
pub email: String, pub email: String,
pub url: String, pub url: String,
pub host: String pub host: String
} }
#[derive(Serialize, Deserialize)]
pub struct CertificateNetwork { pub struct CertificateNetwork {
pub id: NetworkId, pub id: NetworkId,
pub controller: Fingerprint pub controller: Fingerprint
} }
#[derive(Serialize, Deserialize)]
pub struct CertificateIdentity { pub struct CertificateIdentity {
pub identity: Identity, pub identity: Identity,
pub locator: Locator pub locator: Locator
} }
#[derive(Serialize, Deserialize)]
pub struct CertificateSubjectUniqueIdSecret {
pub public: Box<[u8]>,
pub private: Box<[u8]>,
pub type_: CertificateUniqueIdType
}
#[allow(non_snake_case)]
#[derive(Serialize, Deserialize)]
pub struct CertificateSubject { pub struct CertificateSubject {
pub timestamp: i64, pub timestamp: i64,
pub identities: Box<[CertificateIdentity]>, pub identities: Box<[CertificateIdentity]>,
pub networks: Box<[CertificateNetwork]>, pub networks: Box<[CertificateNetwork]>,
pub certificates: Box<[[u8; 48]]>, pub certificates: Box<[Box<[u8]>]>,
pub update_urls: Box<[String]>, pub updateURLs: Box<[String]>,
pub name: CertificateName, pub name: CertificateName,
pub unique_id: Box<[u8]>, pub uniqueId: Box<[u8]>,
pub unique_id_proof_signature: Box<[u8]> pub uniqueIdProofSignature: Box<[u8]>
} }
#[allow(non_snake_case)]
#[derive(Serialize, Deserialize)]
pub struct Certificate { pub struct Certificate {
pub serial_no: [u8; 48], pub serialNo: Box<[u8]>,
pub flags: u64, pub flags: u64,
pub timestamp: i64, pub timestamp: i64,
pub validity: [i64; 2], pub validity: [i64; 2],
pub subject: CertificateSubject, pub subject: CertificateSubject,
pub issuer: Identity, pub issuer: Identity,
pub issuer_name: CertificateName, pub issuerName: CertificateName,
pub extended_attributes: Box<[u8]>, pub extendedAttributes: Box<[u8]>,
pub max_path_length: u32, pub maxPathLength: u32,
pub crl: Box<[u8; 48]>, pub crl: Box<[Box<[u8]>]>,
pub signature: Box<[u8]> pub signature: Box<[u8]>
} }
@ -58,15 +176,15 @@ impl CertificateName {
pub(crate) fn new_from_capi(cn: &ztcore::ZT_Certificate_Name) -> CertificateName { pub(crate) fn new_from_capi(cn: &ztcore::ZT_Certificate_Name) -> CertificateName {
unsafe { unsafe {
return CertificateName { return CertificateName {
serial_no: String::from(CStr::from_ptr(cn.serialNo.as_ptr()).to_str().unwrap()), serialNo: String::from(CStr::from_ptr(cn.serialNo.as_ptr()).to_str().unwrap()),
common_name: String::from(CStr::from_ptr(cn.commonName.as_ptr()).to_str().unwrap()), commonName: String::from(CStr::from_ptr(cn.commonName.as_ptr()).to_str().unwrap()),
country: String::from(CStr::from_ptr(cn.country.as_ptr()).to_str().unwrap()), country: String::from(CStr::from_ptr(cn.country.as_ptr()).to_str().unwrap()),
organization: String::from(CStr::from_ptr(cn.organization.as_ptr()).to_str().unwrap()), organization: String::from(CStr::from_ptr(cn.organization.as_ptr()).to_str().unwrap()),
unit: String::from(CStr::from_ptr(cn.unit.as_ptr()).to_str().unwrap()), unit: String::from(CStr::from_ptr(cn.unit.as_ptr()).to_str().unwrap()),
locality: String::from(CStr::from_ptr(cn.locality.as_ptr()).to_str().unwrap()), locality: String::from(CStr::from_ptr(cn.locality.as_ptr()).to_str().unwrap()),
province: String::from(CStr::from_ptr(cn.province.as_ptr()).to_str().unwrap()), province: String::from(CStr::from_ptr(cn.province.as_ptr()).to_str().unwrap()),
street_address: String::from(CStr::from_ptr(cn.streetAddress.as_ptr()).to_str().unwrap()), streetAddress: String::from(CStr::from_ptr(cn.streetAddress.as_ptr()).to_str().unwrap()),
postal_code: String::from(CStr::from_ptr(cn.postalCode.as_ptr()).to_str().unwrap()), postalCode: String::from(CStr::from_ptr(cn.postalCode.as_ptr()).to_str().unwrap()),
email: String::from(CStr::from_ptr(cn.email.as_ptr()).to_str().unwrap()), email: String::from(CStr::from_ptr(cn.email.as_ptr()).to_str().unwrap()),
url: String::from(CStr::from_ptr(cn.url.as_ptr()).to_str().unwrap()), url: String::from(CStr::from_ptr(cn.url.as_ptr()).to_str().unwrap()),
host: String::from(CStr::from_ptr(cn.host.as_ptr()).to_str().unwrap()) host: String::from(CStr::from_ptr(cn.host.as_ptr()).to_str().unwrap())
@ -78,9 +196,9 @@ impl CertificateName {
impl CertificateNetwork { impl CertificateNetwork {
pub(crate) fn new_from_capi(cn: &ztcore::ZT_Certificate_Network) -> CertificateNetwork { pub(crate) fn new_from_capi(cn: &ztcore::ZT_Certificate_Network) -> CertificateNetwork {
CertificateNetwork{ CertificateNetwork{
id: cn.id as NetworkId, id: NetworkId(cn.id),
controller: Fingerprint{ controller: Fingerprint{
address: cn.controller.address as Address, address: Address(cn.controller.address),
hash: cn.controller.hash hash: cn.controller.hash
} }
} }
@ -112,11 +230,11 @@ impl CertificateSubject {
} }
let ccertificates: &[*const u8] = std::slice::from_raw_parts(cs.certificates, cs.certificateCount as usize); let ccertificates: &[*const u8] = std::slice::from_raw_parts(cs.certificates, cs.certificateCount as usize);
let mut certificates: Vec<[u8; 48]> = Vec::new(); let mut certificates: Vec<Box<[u8]>> = Vec::new();
let mut ctmp: [u8; 48] = [0; 48]; let mut ctmp: [u8; 48] = [0; 48];
for i in ccertificates.iter() { for i in ccertificates.iter() {
copy_nonoverlapping(*i, ctmp.as_mut_ptr(), 48); copy_nonoverlapping(*i, ctmp.as_mut_ptr(), 48);
certificates.push(ctmp.clone()); certificates.push(Box::from(ctmp));
} }
let cupdate_urls: &[*const c_char] = std::slice::from_raw_parts(cs.updateURLs, cs.updateURLCount as usize); let cupdate_urls: &[*const c_char] = std::slice::from_raw_parts(cs.updateURLs, cs.updateURLCount as usize);
@ -130,14 +248,79 @@ impl CertificateSubject {
identities: identities.into_boxed_slice(), identities: identities.into_boxed_slice(),
networks: networks.into_boxed_slice(), networks: networks.into_boxed_slice(),
certificates: certificates.into_boxed_slice(), certificates: certificates.into_boxed_slice(),
update_urls: update_urls.into_boxed_slice(), updateURLs: update_urls.into_boxed_slice(),
name: CertificateName::new_from_capi(&cs.name), name: CertificateName::new_from_capi(&cs.name),
unique_id: Box::from(std::slice::from_raw_parts(cs.uniqueId, cs.uniqueIdSize as usize).clone()), uniqueId: Box::from(std::slice::from_raw_parts(cs.uniqueId, cs.uniqueIdSize as usize).clone()),
unique_id_proof_signature: Box::from(std::slice::from_raw_parts(cs.uniqueIdProofSignature, cs.uniqueIdProofSignatureSize as usize).clone()) uniqueIdProofSignature: Box::from(std::slice::from_raw_parts(cs.uniqueIdProofSignature, cs.uniqueIdProofSignatureSize as usize).clone())
} }
} }
} }
} }
impl Certificate { impl Certificate {
pub(crate) fn new_from_capi(c: &ztcore::ZT_Certificate) -> Certificate {
unsafe {
let cextended_attributes: &[u8] = std::slice::from_raw_parts(c.extendedAttributes, c.extendedAttributesSize as usize);
let mut extended_attributes: Vec<u8> = Vec::new();
extended_attributes.extend(cextended_attributes.iter());
let ccrl: &[*const u8] = std::slice::from_raw_parts(c.crl, c.crlCount as usize);
let mut crl: Vec<Box<[u8]>> = Vec::new();
let mut ctmp: [u8; 48] = [0; 48];
for i in ccrl.iter() {
copy_nonoverlapping(*i, ctmp.as_mut_ptr(), 48);
crl.push(Box::from(ctmp));
}
let csignature: &[u8] = std::slice::from_raw_parts(c.signature, c.signatureSize as usize);
let mut signature: Vec<u8> = Vec::new();
signature.extend(csignature.iter());
return Certificate{
serialNo: Box::from(c.serialNo),
flags: c.flags,
timestamp: c.timestamp,
validity: c.validity,
subject: CertificateSubject::new_from_capi(&c.subject),
issuer: Identity::new_from_capi(c.issuer, false),
issuerName: CertificateName::new_from_capi(&c.issuerName),
extendedAttributes: extended_attributes.into_boxed_slice(),
maxPathLength: c.maxPathLength as u32,
crl: crl.into_boxed_slice(),
signature: signature.into_boxed_slice()
}
}
}
} }
impl CertificateSubjectUniqueIdSecret {
pub fn new(t: CertificateUniqueIdType) -> Self {
unsafe {
let mut unique_id: Vec<u8> = Vec::new();
let mut unique_id_private: Vec<u8> = Vec::new();
unique_id.resize(128, 0);
unique_id_private.resize(128, 0);
let mut unique_id_size: c_int = 128;
let mut unique_id_private_size: c_int = 128;
let ct: ztcore::ZT_CertificateUniqueIdType = num_traits::ToPrimitive::to_u32(&t).unwrap();
if ztcore::ZT_Certificate_newSubjectUniqueId(ct, unique_id.as_mut_ptr() as *mut c_void, &mut unique_id_size as *mut c_int, unique_id_private.as_mut_ptr() as *mut c_void, &mut unique_id_private_size as *mut c_int) != 0 {
panic!("fatal internal error: ZT_Certificate_newSubjectUniqueId failed.");
}
unique_id.resize(unique_id_size as usize, 0);
unique_id_private.resize(unique_id_private_size as usize, 0);
return CertificateSubjectUniqueIdSecret{
public: unique_id.into_boxed_slice(),
private: unique_id_private.into_boxed_slice(),
type_: num_traits::FromPrimitive::from_u32(ct as u32).unwrap()
};
}
}
}
implement_json_serializable!(CertificateName);
implement_json_serializable!(CertificateNetwork);
implement_json_serializable!(CertificateIdentity);
implement_json_serializable!(CertificateSubject);
implement_json_serializable!(Certificate);
implement_json_serializable!(CertificateSubjectUniqueIdSecret);

View file

@ -6,11 +6,21 @@ use std::ffi::CStr;
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
pub struct Endpoint { pub struct Endpoint {
pub ep_type: EndpointType, pub type_: EndpointType,
intl: ztcore::ZT_Endpoint intl: ztcore::ZT_Endpoint
} }
impl Endpoint { impl Endpoint {
#[inline]
pub(crate) fn new_from_capi(ep: *const ztcore::ZT_Endpoint) -> Endpoint {
unsafe {
return Endpoint{
type_: EndpointType::from_u32((*ep).type_ as u32).unwrap(),
intl: *ep
};
}
}
pub fn new_from_string(s: &str) -> Result<Endpoint, ResultCode> { pub fn new_from_string(s: &str) -> Result<Endpoint, ResultCode> {
unsafe { unsafe {
let mut cep: MaybeUninit<ztcore::ZT_Endpoint> = MaybeUninit::uninit(); let mut cep: MaybeUninit<ztcore::ZT_Endpoint> = MaybeUninit::uninit();
@ -18,22 +28,13 @@ impl Endpoint {
if ec == 0 { if ec == 0 {
let epi = cep.assume_init(); let epi = cep.assume_init();
return Ok(Endpoint{ return Ok(Endpoint{
ep_type: EndpointType::from_u32(epi.type_ as u32).unwrap(), type_: EndpointType::from_u32(epi.type_ as u32).unwrap(),
intl: epi intl: epi
}); });
} }
return Err(ResultCode::from_i32(ec).unwrap()); return Err(ResultCode::from_i32(ec).unwrap());
} }
} }
pub(crate) fn new_from_capi(ep: *const ztcore::ZT_Endpoint) -> Endpoint {
unsafe {
return Endpoint{
ep_type: EndpointType::from_u32((*ep).type_ as u32).unwrap(),
intl: *ep
};
}
}
} }
impl ToString for Endpoint { impl ToString for Endpoint {
@ -47,3 +48,33 @@ impl ToString for Endpoint {
} }
} }
} }
impl serde::Serialize for Endpoint {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
serializer.serialize_str(self.to_string().as_str())
}
}
struct EndpointVisitor;
impl<'de> serde::de::Visitor<'de> for EndpointVisitor {
type Value = Endpoint;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("Endpoint value in string form")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error {
let id = Endpoint::new_from_string(s);
if id.is_err() {
return Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self));
}
return Ok(id.ok().unwrap() as Self::Value);
}
}
impl<'de> serde::Deserialize<'de> for Endpoint {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
deserializer.deserialize_str(EndpointVisitor)
}
}

View file

@ -16,7 +16,7 @@ impl Fingerprint {
if ztcore::ZT_Fingerprint_fromString(cfp.as_mut_ptr(), s.as_ptr() as *const c_char) != 0 { if ztcore::ZT_Fingerprint_fromString(cfp.as_mut_ptr(), s.as_ptr() as *const c_char) != 0 {
let fp = cfp.assume_init(); let fp = cfp.assume_init();
return Ok(Fingerprint{ return Ok(Fingerprint{
address: fp.address as Address, address: Address(fp.address),
hash: fp.hash hash: fp.hash
}); });
} }
@ -30,7 +30,7 @@ impl ToString for Fingerprint {
let mut buf: [u8; 256] = [0; 256]; let mut buf: [u8; 256] = [0; 256];
unsafe { unsafe {
if ztcore::ZT_Fingerprint_toString(&ztcore::ZT_Fingerprint { if ztcore::ZT_Fingerprint_toString(&ztcore::ZT_Fingerprint {
address: self.address, address: self.address.0,
hash: self.hash hash: self.hash
}, buf.as_mut_ptr() as *mut c_char, buf.len() as c_int).is_null() { }, buf.as_mut_ptr() as *mut c_char, buf.len() as c_int).is_null() {
return String::from("(invalid)"); return String::from("(invalid)");
@ -39,3 +39,33 @@ impl ToString for Fingerprint {
} }
} }
} }
impl serde::Serialize for Fingerprint {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
serializer.serialize_str(self.to_string().as_str())
}
}
struct FingerprintVisitor;
impl<'de> serde::de::Visitor<'de> for FingerprintVisitor {
type Value = Fingerprint;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("ZeroTier Fingerprint in string format")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error {
let id = Fingerprint::new_from_string(s);
if id.is_err() {
return Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self));
}
return Ok(id.ok().unwrap() as Self::Value);
}
}
impl<'de> serde::Deserialize<'de> for Fingerprint {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
deserializer.deserialize_str(FingerprintVisitor)
}
}

View file

@ -4,11 +4,17 @@ use std::os::raw::*;
use std::ffi::CStr; use std::ffi::CStr;
use num_traits::{ToPrimitive, FromPrimitive}; use num_traits::{ToPrimitive, FromPrimitive};
#[derive(FromPrimitive,ToPrimitive)]
pub enum IdentityType {
Curve25519 = ztcore::ZT_IdentityType_ZT_IDENTITY_TYPE_C25519 as isize,
NistP384 = ztcore::ZT_IdentityType_ZT_IDENTITY_TYPE_P384 as isize,
}
pub struct Identity { pub struct Identity {
pub id_type: IdentityType, pub type_: IdentityType,
pub address: Address, pub address: Address,
capi: *const ztcore::ZT_Identity, capi: *const ztcore::ZT_Identity,
requires_delete: bool requires_delete: bool,
} }
impl Identity { impl Identity {
@ -16,16 +22,18 @@ impl Identity {
unsafe { unsafe {
let idt = ztcore::ZT_Identity_type(id); let idt = ztcore::ZT_Identity_type(id);
let a = ztcore::ZT_Identity_address(id); let a = ztcore::ZT_Identity_address(id);
return Identity{ return Identity {
id_type: FromPrimitive::from_u32(idt as u32).unwrap(), type_: FromPrimitive::from_u32(idt as u32).unwrap(),
address: a as Address, address: Address(a),
capi: id, capi: id,
requires_delete: requires_delete requires_delete: requires_delete,
}; };
} }
} }
pub fn generate(id_type: IdentityType) -> Result<Identity, ResultCode> { /// Generate a new identity.
/// This is time consuming due to one time proof of work. It can take several seconds.
pub fn new_generate(id_type: IdentityType) -> Result<Identity, ResultCode> {
unsafe { unsafe {
let id = ztcore::ZT_Identity_new(id_type.to_u32().unwrap()); let id = ztcore::ZT_Identity_new(id_type.to_u32().unwrap());
if id.is_null() { if id.is_null() {
@ -35,7 +43,8 @@ impl Identity {
} }
} }
pub fn new_from_string(s: String) -> Result<Identity, ResultCode> { /// Construct from a string representation of this identity.
pub fn new_from_string(s: &str) -> Result<Identity, ResultCode> {
unsafe { unsafe {
let id = ztcore::ZT_Identity_fromString(s.as_ptr() as *const c_char); let id = ztcore::ZT_Identity_fromString(s.as_ptr() as *const c_char);
if id.is_null() { if id.is_null() {
@ -45,7 +54,7 @@ impl Identity {
} }
} }
pub fn to_string(&self, include_private: bool) -> String { fn intl_to_string(&self, include_private: bool) -> String {
let mut buf: [u8; 2048] = [0; 2048]; let mut buf: [u8; 2048] = [0; 2048];
unsafe { unsafe {
if ztcore::ZT_Identity_toString(self.capi, buf.as_mut_ptr() as *mut c_char, buf.len() as c_int, if include_private { 1 } else { 0 }).is_null() { if ztcore::ZT_Identity_toString(self.capi, buf.as_mut_ptr() as *mut c_char, buf.len() as c_int, if include_private { 1 } else { 0 }).is_null() {
@ -55,6 +64,14 @@ impl Identity {
} }
} }
/// Convert to a string and include the private key if present.
/// If the private key is not present this is the same as to_string().
#[inline]
pub fn to_secret_string(&self) -> String {
self.intl_to_string(true)
}
/// Validate this identity, which can be slightly time consuming in some cases (20-40ms).
pub fn validate(&self) -> bool { pub fn validate(&self) -> bool {
unsafe { unsafe {
if ztcore::ZT_Identity_validate(self.capi) != 0 { if ztcore::ZT_Identity_validate(self.capi) != 0 {
@ -64,6 +81,7 @@ impl Identity {
false false
} }
/// Returns true if this Identity includes its corresponding private key.
pub fn has_private(&self) -> bool { pub fn has_private(&self) -> bool {
unsafe { unsafe {
if ztcore::ZT_Identity_hasPrivate(self.capi) != 0 { if ztcore::ZT_Identity_hasPrivate(self.capi) != 0 {
@ -73,16 +91,18 @@ impl Identity {
false false
} }
/// Obtain the full fingerprint of this identity, which includes a SHA384 hash of the public key.
pub fn fingerprint(&self) -> Fingerprint { pub fn fingerprint(&self) -> Fingerprint {
unsafe { unsafe {
let cfp = ztcore::ZT_Identity_fingerprint(self.capi); let cfp = ztcore::ZT_Identity_fingerprint(self.capi);
return Fingerprint { return Fingerprint {
address: (*cfp).address, address: Address((*cfp).address),
hash: (*cfp).hash hash: (*cfp).hash,
} };
} }
} }
/// Sign some data with this identity.
pub fn sign(&self, data: &[u8]) -> Result<Box<[u8]>, ResultCode> { pub fn sign(&self, data: &[u8]) -> Result<Box<[u8]>, ResultCode> {
unsafe { unsafe {
let mut sig: Vec<u8> = vec!(0; 128); let mut sig: Vec<u8> = vec!(0; 128);
@ -95,6 +115,7 @@ impl Identity {
} }
} }
/// Verify a signature by this identity.
pub fn verify(&self, data: &[u8], signature: &[u8]) -> bool { pub fn verify(&self, data: &[u8], signature: &[u8]) -> bool {
if signature.len() == 0 { if signature.len() == 0 {
return false; return false;
@ -110,7 +131,7 @@ impl Identity {
impl Drop for Identity { impl Drop for Identity {
fn drop(&mut self) { fn drop(&mut self) {
if self.requires_delete && !self.capi.is_null() { if self.requires_delete {
unsafe { unsafe {
ztcore::ZT_Identity_delete(self.capi); ztcore::ZT_Identity_delete(self.capi);
} }
@ -119,13 +140,38 @@ impl Drop for Identity {
} }
impl ToString for Identity { impl ToString for Identity {
#[inline]
fn to_string(&self) -> String { fn to_string(&self) -> String {
self.to_string(false) self.intl_to_string(false)
} }
} }
impl From<Identity> for String { impl serde::Serialize for Identity {
fn from(id: Identity) -> String { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
id.to_string(false) serializer.serialize_str(self.intl_to_string(false).as_str())
}
}
struct IdentityVisitor;
impl<'de> serde::de::Visitor<'de> for IdentityVisitor {
type Value = Identity;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("ZeroTier Identity in string format")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error {
let id = Identity::new_from_string(s);
if id.is_err() {
return Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self));
}
return Ok(id.ok().unwrap() as Self::Value);
}
}
impl<'de> serde::Deserialize<'de> for Identity {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
deserializer.deserialize_str(IdentityVisitor)
} }
} }

View file

@ -7,16 +7,18 @@ mod certificate;
mod networkid; mod networkid;
mod locator; mod locator;
pub use identity::Identity; pub use identity::*;
pub use address::*; pub use address::*;
pub use fingerprint::Fingerprint; pub use fingerprint::*;
pub use endpoint::Endpoint; pub use endpoint::*;
pub use networkid::*; pub use networkid::*;
pub use locator::Locator; pub use locator::*;
pub use certificate::*;
use bindings::capi as ztcore; use bindings::capi as ztcore;
use num_derive::FromPrimitive; use num_derive::FromPrimitive;
use num_derive::ToPrimitive; use num_derive::ToPrimitive;
use std::os::raw::c_int;
pub const DEFAULT_PORT: u16 = ztcore::ZT_DEFAULT_PORT as u16; pub const DEFAULT_PORT: u16 = ztcore::ZT_DEFAULT_PORT as u16;
@ -45,38 +47,6 @@ pub mod RulePacketCharacteristics {
pub const TcpFlagFIN: u64 = crate::bindings::capi::ZT_RULE_PACKET_CHARACTERISTICS_TCP_FIN as u64; pub const TcpFlagFIN: u64 = crate::bindings::capi::ZT_RULE_PACKET_CHARACTERISTICS_TCP_FIN as u64;
} }
#[derive(FromPrimitive,ToPrimitive)]
pub enum IdentityType {
Curve25519 = ztcore::ZT_IdentityType_ZT_IDENTITY_TYPE_C25519 as isize,
NistP384 = ztcore::ZT_IdentityType_ZT_IDENTITY_TYPE_P384 as isize,
}
pub const CERTIFICATE_MAX_STRING_LENGTH: u32 = ztcore::ZT_CERTIFICATE_MAX_STRING_LENGTH;
pub const CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA: u32 = ztcore::ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA;
pub const CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET: u32 = ztcore::ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET;
pub const CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE: u32 = ztcore::ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE;
pub const CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE: u32 = ztcore::ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE;
#[derive(FromPrimitive,ToPrimitive)]
pub enum CertificateUniqueIdType {
NistP384 = ztcore::ZT_CertificateUniqueIdType_ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384 as isize
}
#[derive(FromPrimitive,ToPrimitive)]
pub enum CertificateError {
None = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_NONE as isize,
HaveNewerCert = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_HAVE_NEWER_CERT as isize,
InvalidFormat = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_INVALID_FORMAT as isize,
InvalidIdentity = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_INVALID_IDENTITY as isize,
InvalidPrimarySignature = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE as isize,
InvalidChain = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_INVALID_CHAIN as isize,
InvalidComponentSignature = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE as isize,
InvalidUniqueIdProof = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF as isize,
MissingRequiredFields = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS as isize,
OutOfValidTimeWindow = ztcore::ZT_CertificateError_ZT_CERTIFICATE_ERROR_OUT_OF_VALID_TIME_WINDOW as isize,
}
#[derive(FromPrimitive,ToPrimitive)] #[derive(FromPrimitive,ToPrimitive)]
pub enum CredentialType { pub enum CredentialType {
Null = ztcore::ZT_CredentialType_ZT_CREDENTIAL_TYPE_NULL as isize, Null = ztcore::ZT_CredentialType_ZT_CREDENTIAL_TYPE_NULL as isize,
@ -242,6 +212,40 @@ pub enum StateObjectType {
Certificate = ztcore::ZT_StateObjectType_ZT_STATE_OBJECT_CERT as isize Certificate = ztcore::ZT_StateObjectType_ZT_STATE_OBJECT_CERT as isize
} }
pub fn version() -> (u32, u32, u32, u32) {
let mut major: c_int = 0;
let mut minor: c_int = 0;
let mut revision: c_int = 0;
let mut build: c_int = 0;
unsafe {
ztcore::ZT_version(&mut major as *mut c_int, &mut minor as *mut c_int, &mut revision as *mut c_int, &mut build as *mut c_int);
}
(major as u32, minor as u32, revision as u32, build as u32)
}
#[macro_export]
macro_rules! implement_json_serializable {
($struct_name:ident) => {
impl $struct_name {
pub fn new_from_json(json: &str) -> Result<$struct_name, String> {
let r: serde_json::error::Result<$struct_name> = serde_json::from_str(json);
if r.is_err() {
let e = r.err();
if e.is_none() {
return Err(String::from("unknown error"));
}
return Err(e.unwrap().to_string());
}
Ok(r.unwrap())
}
pub fn to_json(&self) -> String {
serde_json::to_string_pretty(self).unwrap()
}
}
};
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#[test] #[test]

View file

@ -9,6 +9,7 @@ pub struct Locator {
} }
impl Locator { impl Locator {
#[inline]
pub(crate) fn new_from_capi(l: *const ztcore::ZT_Locator, requires_delete: bool) -> Locator { pub(crate) fn new_from_capi(l: *const ztcore::ZT_Locator, requires_delete: bool) -> Locator {
Locator{ Locator{
capi: l, capi: l,
@ -66,3 +67,33 @@ impl ToString for Locator {
} }
} }
} }
impl serde::Serialize for Locator {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
serializer.serialize_str(self.to_string().as_str())
}
}
struct LocatorVisitor;
impl<'de> serde::de::Visitor<'de> for LocatorVisitor {
type Value = Locator;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("Locator value in string form")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error {
let id = Locator::new_from_string(s);
if id.is_err() {
return Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self));
}
return Ok(id.ok().unwrap() as Self::Value);
}
}
impl<'de> serde::Deserialize<'de> for Locator {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
deserializer.deserialize_str(LocatorVisitor)
}
}

View file

@ -1,9 +1,41 @@
pub type NetworkId = u64; pub struct NetworkId(pub u64);
pub fn network_id_to_string(n: NetworkId) -> String { impl NetworkId {
format!("{:0>16x}", n as u64) #[inline]
pub fn new_from_string(s: &str) -> NetworkId {
return NetworkId(u64::from_str_radix(s, 16).unwrap_or(0));
}
} }
pub fn network_id_from_string(s: &str) -> NetworkId { impl ToString for NetworkId {
return u64::from_str_radix(s, 16).unwrap_or(0) as NetworkId; #[inline]
fn to_string(&self) -> String {
format!("{:0>16x}", self.0)
}
}
impl serde::Serialize for NetworkId {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
serializer.serialize_str(self.to_string().as_str())
}
}
struct NetworkIdVisitor;
impl<'de> serde::de::Visitor<'de> for NetworkIdVisitor {
type Value = NetworkId;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("ZeroTier Address in string format")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error {
Ok(NetworkId::new_from_string(s))
}
}
impl<'de> serde::Deserialize<'de> for NetworkId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
deserializer.deserialize_str(NetworkIdVisitor)
}
} }