Plumb cert changes through to Rust.

This commit is contained in:
Adam Ierymenko 2021-04-19 16:17:31 -04:00
parent e7ed8051c6
commit 8153211b26
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
7 changed files with 199 additions and 316 deletions

View file

@ -67,14 +67,6 @@ Certificate &Certificate::operator=(const ZT_Certificate &cert)
}
}
if (cert.subject.certificates != nullptr) {
for (unsigned int i = 0; i < cert.subject.certificateCount; ++i) {
if (cert.subject.certificates[i]) {
addSubjectCertificate(cert.subject.certificates[i]);
}
}
}
if (cert.subject.updateURLs != nullptr) {
for (unsigned int i = 0; i < cert.subject.updateURLCount; ++i) {
if (cert.subject.updateURLs[i]) {
@ -85,7 +77,6 @@ Certificate &Certificate::operator=(const ZT_Certificate &cert)
this->subject.identityCount = cert.subject.identityCount;
this->subject.networkCount = cert.subject.networkCount;
this->subject.certificateCount = cert.subject.certificateCount;
this->subject.updateURLCount = cert.subject.updateURLCount;
Utils::copy< sizeof(ZT_Certificate_Name) >(&(this->subject.name), &(cert.subject.name));
@ -160,18 +151,6 @@ ZT_Certificate_Network *Certificate::addSubjectNetwork(const uint64_t id, const
return &(m_subjectNetworks.back());
}
void Certificate::addSubjectCertificate(const uint8_t serialNo[ZT_SHA384_DIGEST_SIZE])
{
// Store local copy of serial in m_serials container.
m_serials.push_front(H384(serialNo));
// Enlarge array of uint8_t pointers, set new pointer to local copy of serial, and set
// certificates to point to potentially reallocated array.
m_subjectCertificates.push_back(reinterpret_cast<const uint8_t *>(m_serials.front().data));
this->subject.certificates = m_subjectCertificates.data();
this->subject.certificateCount = (unsigned int)m_subjectCertificates.size();
}
void Certificate::addSubjectUpdateUrl(const char *url)
{
if ((url != nullptr) && (url[0] != 0)) {
@ -282,14 +261,6 @@ bool Certificate::decode(const void *const data, const unsigned int len)
this->addSubjectNetwork(nwid, fp);
}
cnt = (unsigned int)d.getUI("s.c$");
for (unsigned int i = 0; i < cnt; ++i) {
const Vector< uint8_t > &serial = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.c$", i)];
if (serial.size() != ZT_SHA384_DIGEST_SIZE)
return false;
this->addSubjectCertificate(serial.data());
}
cnt = (unsigned int)d.getUI("s.u$");
for (unsigned int i = 0; i < cnt; ++i)
addSubjectUpdateUrl(d.getS(Dictionary::arraySubscript(tmp, sizeof(tmp), "s.u$", i), tmp2, sizeof(tmp2)));
@ -529,12 +500,10 @@ void Certificate::m_clear()
m_identities.clear();
m_locators.clear();
m_strings.clear();
m_serials.clear();
m_subjectIdentities.clear();
m_subjectNetworks.clear();
m_updateUrls.clear();
m_subjectCertificates.clear();
m_extendedAttributes.clear();
}
@ -592,14 +561,6 @@ void Certificate::m_encodeSubject(const ZT_Certificate_Subject &s, Dictionary &d
}
}
if (s.certificates) {
d.add("s.c$", (uint64_t)s.certificateCount);
for (unsigned int i = 0; i < s.certificateCount; ++i) {
if (s.certificates[i])
d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.c$", i)].assign(s.certificates[i], s.certificates[i] + ZT_SHA384_DIGEST_SIZE);
}
}
if (s.updateURLs) {
d.add("s.u$", (uint64_t)s.updateURLCount);
for (unsigned int i = 0; i < s.updateURLCount; ++i)

View file

@ -91,13 +91,6 @@ public:
*/
ZT_Certificate_Network *addSubjectNetwork(uint64_t id, const ZT_Fingerprint &controller);
/**
* Add a subject certificate (by its serial number)
*
* @param serialNo 384-bit serial number
*/
void addSubjectCertificate(const uint8_t serialNo[ZT_SHA384_DIGEST_SIZE]);
/**
* Add an update URL to the updateUrls list
*
@ -215,12 +208,10 @@ private:
ForwardList< Identity > m_identities;
ForwardList< Locator > m_locators;
ForwardList< String > m_strings;
ForwardList< H384 > m_serials;
// These are stored in a vector because the memory needs to be contiguous.
Vector< ZT_Certificate_Identity > m_subjectIdentities;
Vector< ZT_Certificate_Network > m_subjectNetworks;
Vector< const uint8_t * > m_subjectCertificates;
Vector< const char * > m_updateUrls;
Vector< uint8_t > m_extendedAttributes;
};

View file

@ -344,15 +344,6 @@ static bool ZTT_deepCompareCertificates(const Certificate &a, const Certificate
return false;
}
for (unsigned int i = 0; i < a.subject.certificateCount; ++i) {
if ((!a.subject.certificates) || (!b.subject.certificates))
return false;
if ((!a.subject.certificates[i]) || (!b.subject.certificates[i]))
return false;
if (memcmp(a.subject.certificates[i], b.subject.certificates[i], ZT_SHA384_DIGEST_SIZE) != 0)
return false;
}
for (unsigned int i = 0; i < a.subject.updateURLCount; ++i) {
if ((!a.subject.updateURLs) || (!b.subject.updateURLs))
return false;

View file

@ -503,11 +503,6 @@ typedef struct
*/
ZT_Certificate_Network *networks;
/**
* Serial numbers of other certificates being signed (each is 48 bytes / 384 bits)
*/
const uint8_t *const *certificates;
/**
* URLs that can be consulted for updates to this certificate.
*/
@ -523,11 +518,6 @@ typedef struct
*/
unsigned int networkCount;
/**
* Number of certificates
*/
unsigned int certificateCount;
/**
* Number of update URLs
*/

View file

@ -13,7 +13,9 @@
use std::ffi::CString;
use std::hash::{Hash, Hasher};
use std::mem::zeroed;
use std::os::raw::{c_char, c_uint, c_void};
use std::pin::Pin;
use std::ptr::{copy_nonoverlapping, null, null_mut};
use num_derive::{FromPrimitive, ToPrimitive};
@ -22,7 +24,6 @@ use serde::{Deserialize, Serialize};
use crate::*;
use crate::capi as ztcore;
use std::mem::zeroed;
/// Maximum length of a string in a certificate (mostly for the certificate name fields).
pub const CERTIFICATE_MAX_STRING_LENGTH: isize = ztcore::ZT_CERTIFICATE_MAX_STRING_LENGTH as isize;
@ -33,11 +34,33 @@ pub const CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA: u32 = ztcore::ZT_CERTIFICATE_LOC
/// 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;
fn vec_to_array<const L: usize>(v: &Vec<u8>) -> [u8; L] {
let mut a = [0_u8; L];
unsafe { copy_nonoverlapping(v.as_ptr(), a.as_mut_ptr(), v.len().min(L)) };
a
}
/// 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;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#[derive(FromPrimitive, PartialEq, Eq, Clone, Copy)]
pub enum CertificatePublicKeyAlgorithm {
None = ztcore::ZT_CertificatePublicKeyAlgorithm_ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_NONE as isize,
ECDSANistP384 = ztcore::ZT_CertificatePublicKeyAlgorithm_ZT_CERTIFICATE_PUBLIC_KEY_ALGORITHM_ECDSA_NIST_P_384 as isize,
}
impl CertificatePublicKeyAlgorithm {
#[inline(always)]
pub fn to_i32(&self) -> i32 {
*self as i32
}
}
impl From<i32> for CertificatePublicKeyAlgorithm {
#[inline(always)]
fn from(n: i32) -> CertificatePublicKeyAlgorithm {
CertificatePublicKeyAlgorithm::from_i32(n).unwrap_or(CertificatePublicKeyAlgorithm::None)
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -84,79 +107,6 @@ impl<'de> serde::Deserialize<'de> for CertificateSerialNo {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> { deserializer.deserialize_str(CertificateSerialNoVisitor) }
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Type of certificate subject unique ID
#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq, Clone, Copy)]
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("object") }
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> where E: serde::de::Error { Self::Value::new_from_string(s).map_or_else(|_| { Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self)) },|id| { Ok(id 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) }
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
pub struct CertificateSubjectUniqueIdSecret {
#[serde(with = "Base64Standard")]
pub public: Vec<u8>,
#[serde(with = "Base64Standard")]
pub private: Vec<u8>,
#[serde(rename = "type")]
pub type_: CertificateUniqueIdType,
}
const CERTIFICATE_UNIQUE_ID_CREATE_BUF_SIZE: usize = 128;
impl CertificateSubjectUniqueIdSecret {
pub fn new(t: CertificateUniqueIdType) -> Self {
let mut unique_id: [u8; CERTIFICATE_UNIQUE_ID_CREATE_BUF_SIZE] = [0; CERTIFICATE_UNIQUE_ID_CREATE_BUF_SIZE];
let mut unique_id_private: [u8; CERTIFICATE_UNIQUE_ID_CREATE_BUF_SIZE] = [0; CERTIFICATE_UNIQUE_ID_CREATE_BUF_SIZE];
let mut unique_id_size = CERTIFICATE_UNIQUE_ID_CREATE_BUF_SIZE as c_int;
let mut unique_id_private_size = CERTIFICATE_UNIQUE_ID_CREATE_BUF_SIZE as c_int;
let ct: ztcore::ZT_CertificateUniqueIdType = num_traits::ToPrimitive::to_u32(&t).unwrap();
unsafe {
if ztcore::ZT_Certificate_newSubjectUniqueId(ct, unique_id.as_mut_ptr() as *mut c_void, &mut unique_id_size, unique_id_private.as_mut_ptr() as *mut c_void, &mut unique_id_private_size) != 0 {
panic!("fatal internal error: ZT_Certificate_newSubjectUniqueId failed.");
}
}
CertificateSubjectUniqueIdSecret {
public: Vec::from(&unique_id[0..unique_id_size as usize]),
private: Vec::from(&unique_id_private[0..unique_id_private_size as usize]),
type_: num_traits::FromPrimitive::from_i32(ct as i32).unwrap(),
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#[derive(FromPrimitive, ToPrimitive, PartialEq, Eq, Clone, Copy)]
@ -204,7 +154,7 @@ impl<S: AsRef<str>> From<S> for CertificateError {
"invaliduniqueidproof" => CertificateError::InvalidUniqueIdProof,
"missingrequiredfields" => CertificateError::MissingRequiredFields,
"outofvalidtimewindow" => CertificateError::OutOfValidTimeWindow,
_ => CertificateError::None // also "none"
_ => CertificateError::None
}
}
}
@ -288,9 +238,7 @@ impl CertificateName {
if l > 126 {
l = 126;
}
unsafe {
copy_nonoverlapping(s.as_ptr(), cs.as_mut_ptr() as *mut u8, l);
}
unsafe { copy_nonoverlapping(s.as_ptr(), cs.as_mut_ptr() as *mut u8, l); }
cs[l + 1] = 0;
}
@ -393,7 +341,6 @@ pub struct CertificateSubject {
pub timestamp: i64,
pub identities: Vec<CertificateIdentity>,
pub networks: Vec<CertificateNetwork>,
pub certificates: Vec<CertificateSerialNo>,
#[serde(rename = "updateURLs")]
pub update_urls: Vec<String>,
pub name: CertificateName,
@ -401,18 +348,17 @@ pub struct CertificateSubject {
#[serde(rename = "uniqueId")]
pub unique_id: Vec<u8>,
#[serde(with = "Base64Standard")]
#[serde(rename = "uniqueIdProofSignature")]
pub unique_id_proof_signature: Vec<u8>,
#[serde(rename = "uniqueIdSignature")]
pub unique_id_signature: Vec<u8>,
}
#[allow(unused)]
pub(crate) struct CertificateSubjectCAPIContainer {
pub(crate) subject: ztcore::ZT_Certificate_Subject,
subject_identities: Vec<ztcore::ZT_Certificate_Identity>,
subject_networks: Vec<ztcore::ZT_Certificate_Network>,
subject_certificates: Vec<*const u8>,
subject_urls: Vec<*const c_char>,
subject_urls_strs: Vec<CString>,
subject_identities: Pin<Box<[ztcore::ZT_Certificate_Identity]>>,
subject_networks: Pin<Box<[ztcore::ZT_Certificate_Network]>>,
subject_urls: Pin<Box<[*const c_char]>>,
subject_urls_strs: Pin<Box<[CString]>>,
}
impl CertificateSubject {
@ -421,11 +367,10 @@ impl CertificateSubject {
timestamp: 0,
identities: Vec::new(),
networks: Vec::new(),
certificates: Vec::new(),
update_urls: Vec::new(),
name: CertificateName::new(),
unique_id: Vec::new(),
unique_id_proof_signature: Vec::new(),
unique_id_signature: Vec::new(),
}
}
@ -449,16 +394,6 @@ impl CertificateSubject {
}
}
let mut certificates: Vec<CertificateSerialNo> = Vec::new();
if !cs.certificates.is_null() && cs.certificateCount > 0 {
let ccertificates: &[*const u8] = std::slice::from_raw_parts(cs.certificates, cs.certificateCount as usize);
let mut ctmp: [u8; 48] = [0; 48];
for i in ccertificates.iter() {
copy_nonoverlapping(*i, ctmp.as_mut_ptr(), 48);
certificates.push(CertificateSerialNo(ctmp));
}
}
let mut update_urls: Vec<String> = Vec::new();
if !cs.updateURLs.is_null() && cs.updateURLCount > 0 {
let cupdate_urls: &[*const c_char] = std::slice::from_raw_parts(cs.updateURLs, cs.updateURLCount as usize);
@ -469,20 +404,18 @@ impl CertificateSubject {
return CertificateSubject {
timestamp: cs.timestamp,
identities: identities,
networks: networks,
certificates: certificates,
update_urls: update_urls,
identities,
networks,
update_urls,
name: CertificateName::new_from_capi(&cs.name),
unique_id: Vec::from(std::slice::from_raw_parts(cs.uniqueId, cs.uniqueIdSize as usize)),
unique_id_proof_signature: Vec::from(std::slice::from_raw_parts(cs.uniqueIdProofSignature, cs.uniqueIdProofSignatureSize as usize)),
unique_id: cs.uniqueId[0..(cs.uniqueIdSize as usize)].to_vec(),
unique_id_signature: cs.uniqueIdSignature[0..cs.uniqueIdSignatureSize as usize].to_vec(),
};
}
pub(crate) unsafe fn to_capi(&self) -> CertificateSubjectCAPIContainer {
let mut capi_identities: Vec<ztcore::ZT_Certificate_Identity> = Vec::new();
let mut capi_networks: Vec<ztcore::ZT_Certificate_Network> = Vec::new();
let mut capi_certificates: Vec<*const u8> = Vec::new();
let mut capi_urls: Vec<*const c_char> = Vec::new();
let mut capi_urls_strs: Vec<CString> = Vec::new();
@ -498,12 +431,6 @@ impl CertificateSubject {
capi_networks.push((*i).to_capi());
}
}
if !self.certificates.is_empty() {
capi_certificates.reserve(self.certificates.len());
for i in self.certificates.iter() {
capi_certificates.push((*i).0.as_ptr());
}
}
if !self.update_urls.is_empty() {
capi_urls.reserve(self.update_urls.len());
capi_urls_strs.reserve(self.update_urls.len());
@ -518,52 +445,54 @@ impl CertificateSubject {
}
}
let mut capi_identities = Pin::from(capi_identities.into_boxed_slice());
let mut capi_networks = Pin::from(capi_networks.into_boxed_slice());
let mut capi_urls = Pin::from(capi_urls.into_boxed_slice());
let mut capi_urls_strs = Pin::from(capi_urls_strs.into_boxed_slice());
CertificateSubjectCAPIContainer {
subject: ztcore::ZT_Certificate_Subject {
timestamp: self.timestamp,
identities: capi_identities.as_mut_ptr(),
networks: capi_networks.as_mut_ptr(),
certificates: capi_certificates.as_ptr(),
updateURLs: capi_urls.as_ptr(),
identityCount: capi_identities.len() as c_uint,
networkCount: capi_networks.len() as c_uint,
certificateCount: capi_certificates.len() as c_uint,
updateURLCount: capi_urls.len() as c_uint,
name: self.name.to_capi(),
uniqueId: self.unique_id.as_ptr(),
uniqueIdProofSignature: self.unique_id_proof_signature.as_ptr(),
uniqueId: vec_to_array(&self.unique_id),
uniqueIdSignature: vec_to_array(&self.unique_id_signature),
uniqueIdSize: self.unique_id.len() as c_uint,
uniqueIdProofSignatureSize: self.unique_id_proof_signature.len() as c_uint,
uniqueIdSignatureSize: self.unique_id_signature.len() as c_uint,
},
subject_identities: capi_identities,
subject_networks: capi_networks,
subject_certificates: capi_certificates,
subject_urls: capi_urls,
subject_urls_strs: capi_urls_strs,
}
}
pub fn new_csr(&self, uid: Option<&CertificateSubjectUniqueIdSecret>) -> Result<Vec<u8>, ResultCode> {
/// Create a new certificate signing request.
/// A CSR is a Certificate containing only the subject (with optional unique ID and signature)
/// and its private key. Other fields must be filled in by the owner of the signing certificate.
pub fn new_csr(&self, certificate_public_key: &[u8], subject_unique_id_private_key: Option<&[u8]>) -> Result<Vec<u8>, ResultCode> {
let mut csr: Vec<u8> = Vec::new();
csr.resize(65536, 0);
let mut csr_size: c_int = 65536;
unsafe {
let capi = self.to_capi();
if uid.is_some() {
let uid2 = uid.unwrap();
if ztcore::ZT_Certificate_newCSR(&capi.subject as *const ztcore::ZT_Certificate_Subject, uid2.public.as_ptr() as *const c_void, uid2.public.len() as c_int, uid2.private.as_ptr() as *const c_void, uid2.private.len() as c_int, csr.as_mut_ptr() as *mut c_void, &mut csr_size) != 0 {
return Err(ResultCode::ErrorBadParameter);
}
} else {
if ztcore::ZT_Certificate_newCSR(&capi.subject, null(), -1, null(), -1, csr.as_mut_ptr() as *mut c_void, &mut csr_size) != 0 {
return Err(ResultCode::ErrorBadParameter);
}
}
}
csr.resize(csr_size as usize, 0);
let (uid, uid_size) = subject_unique_id_private_key.map_or((null::<u8>(), 0 as c_int), |b| (b.as_ptr(), b.len() as c_int));
let r = unsafe {
let s = self.to_capi();
ztcore::ZT_Certificate_newCSR(&s.subject, certificate_public_key.as_ptr().cast(), certificate_public_key.len() as c_int, uid.cast(), uid_size, csr.as_mut_ptr().cast(), &mut csr_size)
};
return Ok(csr);
if r == 0 {
csr.resize(csr_size as usize, 0);
csr.shrink_to_fit();
Ok(csr)
} else {
Err(ResultCode::from_i32(r as i32).unwrap_or(ResultCode::ErrorInternalNonFatal))
}
}
}
@ -577,15 +506,17 @@ pub struct Certificate {
pub timestamp: i64,
pub validity: [i64; 2],
pub subject: CertificateSubject,
pub issuer: Option<Identity>,
#[serde(rename = "issuerName")]
pub issuer_name: CertificateName,
pub issuer: CertificateSerialNo,
#[serde(rename = "issuerPublicKey")]
pub issuer_public_key: Vec<u8>,
#[serde(rename = "publicKey")]
pub public_key: Vec<u8>,
#[serde(rename = "extendedAttributes")]
pub extended_attributes: Vec<u8>,
#[serde(rename = "maxPathLength")]
pub max_path_length: u32,
#[serde(with = "Base64Standard")]
pub signature: Vec<u8>,
#[serde(rename = "maxPathLength")]
pub max_path_length: u32,
}
#[allow(unused)]
@ -595,15 +526,37 @@ pub(crate) struct CertificateCAPIContainer {
}
impl Certificate {
/// Create a new public/private key pair for use in certificate signing or subject unique IDs.
/// This returns a pair of (public, private) or an error. The first byte of both the
/// public and private are the type.
pub fn new_key_pair(alg: CertificatePublicKeyAlgorithm) -> Result<(Vec<u8>, Vec<u8>), ResultCode> {
let mut public_key = [0_u8; ztcore::ZT_CERTIFICATE_MAX_PUBLIC_KEY_SIZE as usize];
let mut private_key = [0_u8; ztcore::ZT_CERTIFICATE_MAX_PRIVATE_KEY_SIZE as usize];
let mut public_key_size: c_int = 0;
let mut private_key_size: c_int = 0;
let r = unsafe { ztcore::ZT_Certificate_newKeyPair(alg.to_i32() as ztcore::ZT_CertificatePublicKeyAlgorithm, public_key.as_mut_ptr(), &mut public_key_size, private_key.as_mut_ptr(), &mut private_key_size) };
if r == 0 {
if public_key_size > 0 && private_key_size > 0 {
Ok((public_key[0..public_key_size as usize].to_vec(), private_key[0..private_key_size as usize].to_vec()))
} else {
Err(ResultCode::ErrorInternalNonFatal)
}
} else {
Err(ResultCode::from_i32(r as i32).unwrap_or(ResultCode::ErrorBadParameter))
}
}
/// Create an empty certificate structure.
pub fn new() -> Certificate {
Certificate {
serial_no: CertificateSerialNo::new(),
flags: 0,
timestamp: 0,
validity: [0, i64::max_value()],
validity: [0, i64::MAX],
subject: CertificateSubject::new(),
issuer: None,
issuer_name: CertificateName::new(),
issuer: CertificateSerialNo::new(),
issuer_public_key: Vec::new(),
public_key: Vec::new(),
extended_attributes: Vec::new(),
max_path_length: 0,
signature: Vec::new(),
@ -617,11 +570,12 @@ impl Certificate {
timestamp: c.timestamp,
validity: c.validity,
subject: CertificateSubject::new_from_capi(&c.subject),
issuer: if c.issuer.is_null() { None } else { Some(Identity::new_from_capi(c.issuer, false).clone()) },
issuer_name: CertificateName::new_from_capi(&c.issuerName),
issuer: CertificateSerialNo(c.issuer),
issuer_public_key: c.issuerPublicKey[0..(c.issuerPublicKeySize as usize)].to_vec(),
public_key: c.publicKey[0..(c.publicKeySize as usize)].to_vec(),
extended_attributes: Vec::from(std::slice::from_raw_parts(c.extendedAttributes, c.extendedAttributesSize as usize)),
max_path_length: c.maxPathLength as u32,
signature: Vec::from(std::slice::from_raw_parts(c.signature, c.signatureSize as usize)),
signature: c.signature[0..(c.signatureSize as usize)].to_vec(),
};
}
@ -634,12 +588,15 @@ impl Certificate {
timestamp: self.timestamp,
validity: self.validity,
subject: subject.subject,
issuer: if self.issuer.is_some() { self.issuer.as_ref().unwrap().capi } else { null() },
issuerName: self.issuer_name.to_capi(),
issuer: self.issuer.0,
issuerPublicKey: vec_to_array(&self.issuer_public_key),
publicKey: vec_to_array(&self.public_key),
issuerPublicKeySize: self.issuer_public_key.len() as c_uint,
publicKeySize: self.public_key.len() as c_uint,
extendedAttributes: self.extended_attributes.as_ptr(),
extendedAttributesSize: self.extended_attributes.len() as c_uint,
maxPathLength: self.max_path_length as c_uint,
signature: self.signature.as_ptr(),
signature: vec_to_array(&self.signature),
signatureSize: self.signature.len() as c_uint,
},
subject_container: subject,
@ -677,23 +634,24 @@ impl Certificate {
return Ok(cert.into_boxed_slice());
}
pub fn sign(&self, id: &Identity) -> Result<Vec<u8>, ResultCode> {
if !id.has_private() {
return Err(ResultCode::ErrorBadParameter);
/// Sign this certificate, returning new signed certificate.
pub fn sign(&self, issuer: &CertificateSerialNo, issuer_private_key: &[u8]) -> Result<Certificate, ResultCode> {
let signed_cert = unsafe {
let c = self.to_capi();
ztcore::ZT_Certificate_sign(&c.certificate, issuer.0.as_ptr(), issuer_private_key.as_ptr().cast(), issuer_private_key.len() as c_int)
};
if signed_cert.is_null() {
Err(ResultCode::ErrorBadParameter)
} else {
let signed_cert2 = unsafe { Certificate::new_from_capi(&*signed_cert) };
unsafe { ztcore::ZT_Certificate_delete(signed_cert) };
Ok(signed_cert2)
}
let mut signed_cert: Vec<u8> = Vec::new();
signed_cert.resize(16384, 0);
let mut signed_cert_size: c_int = 16384;
unsafe {
let capi = self.to_capi();
if ztcore::ZT_Certificate_sign(&capi.certificate as *const ztcore::ZT_Certificate, id.capi, signed_cert.as_mut_ptr() as *mut c_void, &mut signed_cert_size) != 0 {
return Err(ResultCode::ErrorBadParameter);
}
}
signed_cert.resize(signed_cert_size as usize, 0);
return Ok(signed_cert);
}
/// Verify certificate structure and signatures.
/// This does not verify the full certificate chain, just what can be verified
/// by looking at the certificate itself.
pub fn verify(&self, clock: i64) -> CertificateError {
unsafe {
let capi = self.to_capi();
@ -709,18 +667,11 @@ mod tests {
use crate::*;
use num_traits::FromPrimitive;
#[test]
fn certificate_serial_no() {
let test: [u8; 48] = [1; 48];
let sn = CertificateSerialNo::from(&test[0..48]);
assert!(test.eq(&sn.0));
}
#[test]
fn generate_certificate_unique_id() {
let uid = CertificateSubjectUniqueIdSecret::new(CertificateUniqueIdType::NistP384);
println!("certificate unique ID public: {}", hex::encode(uid.public).as_str());
println!("certificate unique ID private: {}", hex::encode(uid.private).as_str());
let (pubk, privk) = Certificate::new_key_pair(CertificatePublicKeyAlgorithm::ECDSANistP384).ok().unwrap();
println!("certificate unique ID public: {}", hex::encode(pubk.as_ref()).as_str());
println!("certificate unique ID private: {}", hex::encode(privk.as_ref()).as_str());
}
#[test]
@ -732,22 +683,27 @@ mod tests {
}
#[test]
fn cert_encode_decode() {
let id0 = Identity::new_generate(IdentityType::NistP384).ok().unwrap();
fn cert() {
let (issuer_pubk, issuer_privk) = Certificate::new_key_pair(CertificatePublicKeyAlgorithm::ECDSANistP384).ok().unwrap();
let (pubk, privk) = Certificate::new_key_pair(CertificatePublicKeyAlgorithm::ECDSANistP384).ok().unwrap();
let (unique_id, unique_id_private) = Certificate::new_key_pair(CertificatePublicKeyAlgorithm::ECDSANistP384).ok().unwrap();
let id0 = Identity::new_generate(IdentityType::Curve25519).ok().unwrap();
let mut cert = Certificate{
serial_no: CertificateSerialNo::new(),
flags: 1,
timestamp: 2,
validity: [ 3,4 ],
validity: [ 1,10 ],
subject: CertificateSubject::new(),
issuer: None,
issuer_name: CertificateName::new(),
issuer: CertificateSerialNo::new(),
issuer_public_key: issuer_pubk,
public_key: pubk,
extended_attributes: Vec::new(),
max_path_length: 123,
signature: Vec::new()
};
cert.serial_no.0[1] = 99;
cert.issuer.0[1] = 199;
cert.subject.timestamp = 5;
cert.subject.identities.push(CertificateIdentity{
identity: id0.clone(),
@ -757,7 +713,6 @@ mod tests {
id: NetworkId(0xdeadbeef),
controller: Some(id0.fingerprint())
});
cert.subject.certificates.push(CertificateSerialNo::new());
cert.subject.update_urls.push(String::from("http://foo.bar"));
cert.subject.name = CertificateName{
serial_no: String::from("12345"),
@ -774,7 +729,7 @@ mod tests {
host: String::from("zerotier.com")
};
//println!("{}", cert.to_json().as_str());
println!("{}", cert.to_json().as_str());
unsafe {
let cert_capi = cert.to_capi();
@ -789,8 +744,7 @@ mod tests {
assert!(cert2.ok().unwrap() == cert);
}
let uid = CertificateSubjectUniqueIdSecret::new(CertificateUniqueIdType::NistP384);
let csr = cert.subject.new_csr(Some(&uid));
let csr = cert.subject.new_csr(pubk.as_ref(), Some(unique_id_private.as_ref()));
assert!(csr.is_ok());
let csr = csr.ok().unwrap();
@ -798,15 +752,12 @@ mod tests {
assert!(csr_decoded.is_ok());
let mut csr_decoded = csr_decoded.ok().unwrap();
let cert_signed = csr_decoded.sign(&id0);
let cert_signed = csr_decoded.sign(&cert.issuer, issuer_privk.as_ref());
assert!(cert_signed.is_ok());
let cert_signed = cert_signed.ok().unwrap();
let cert_signed_decoded = Certificate::new_from_bytes(cert_signed.as_slice(), false);
assert!(cert_signed_decoded.is_ok());
let cert_signed_decoded = cert_signed_decoded.ok().unwrap();
assert!(cert_signed_decoded.signature.len() > 0);
assert!(cert_signed_decoded.verify(-1) == CertificateError::None);
assert!(cert_signed.verify(-1) == CertificateError::None);
assert!(cert_signed.verify(5) == CertificateError::None);
assert!(cert_signed.verify(15) != CertificateError::None);
}
}

View file

@ -29,20 +29,27 @@ fn show<'a>(store: &Arc<Store>, cli_args: &ArgMatches<'a>) -> i32 {
0
}
fn newsid(cli_args: Option<&ArgMatches>) -> i32 {
let sid = CertificateSubjectUniqueIdSecret::new(CertificateUniqueIdType::NistP384); // right now there's only one type
let sid = serde_json::to_string(&sid).unwrap();
let path = cli_args.map_or("", |cli_args| { cli_args.value_of("path").unwrap_or("") });
if path.is_empty() {
let _ = std::io::stdout().write_all(sid.as_bytes());
0
fn newsuid(cli_args: Option<&ArgMatches>) -> i32 {
let key_pair = Certificate::new_key_pair(CertificatePublicKeyAlgorithm::ECDSANistP384);
if key_pair.is_err() {
println!("ERROR: internal error creating key pair: {}", key_pair.err().unwrap().to_str());
1
} else {
std::fs::write(path, sid.as_bytes()).map_or_else(|e| {
eprintln!("FATAL: error writing '{}': {}", path, e.to_string());
e.raw_os_error().unwrap_or(1)
}, |_| {
let (_, privk) = key_pair.ok().unwrap();
let privk_hex = hex::encode(privk);
let path = cli_args.map_or("", |cli_args| { cli_args.value_of("path").unwrap_or("") });
if path.is_empty() {
println!("{}", privk_hex);
0
})
} else {
std::fs::write(path, privk_hex.as_bytes()).map_or_else(|e| {
eprintln!("FATAL: error writing '{}': {}", path, e.to_string());
e.raw_os_error().unwrap_or(1)
}, |_| {
println!("Subject unique ID secret written to: {} (public is included)", path);
0
})
}
}
}
@ -50,29 +57,29 @@ fn newcsr(cli_args: &ArgMatches) -> i32 {
let theme = &dialoguer::theme::SimpleTheme;
let subject_unique_id: String = Input::with_theme(theme)
.with_prompt("Path to subject unique ID secret key (recommended)")
.with_prompt("Path to subject unique ID secret key (empty to create unsigned subject)")
.allow_empty(true)
.interact_text()
.unwrap_or_default();
let subject_unique_id: Option<CertificateSubjectUniqueIdSecret> = if subject_unique_id.is_empty() {
let subject_unique_id_private_key = if subject_unique_id.is_empty() {
None
} else {
let b = crate::utils::read_limit(subject_unique_id, 16384);
let b = crate::utils::read_limit(subject_unique_id, 1024);
if b.is_err() {
println!("ERROR: unable to read subject unique ID secret file: {}", b.err().unwrap().to_string());
return 1;
}
let json = String::from_utf8(b.unwrap());
if json.is_err() {
println!("ERROR: invalid subject unique ID secret: {}", json.err().unwrap().to_string());
let privk_hex = String::from_utf8(b.unwrap());
if privk_hex.is_err() {
println!("ERROR: invalid UTF-8 in secret");
return 1;
}
let sid = serde_json::from_str::<CertificateSubjectUniqueIdSecret>(json.unwrap().as_str());
if sid.is_err() {
println!("ERROR: invalid subject unique ID secret: {}", sid.err().unwrap());
let privk = hex::decode(privk_hex.unwrap().trim());
if privk.is_err() || privk.as_ref().unwrap().is_empty() {
println!("ERROR: invalid unique ID secret: {}", privk.err().unwrap().to_string());
return 1;
}
Some(sid.unwrap())
Some(privk.unwrap())
};
let timestamp: i64 = Input::with_theme(theme)
@ -86,7 +93,7 @@ fn newcsr(cli_args: &ArgMatches) -> i32 {
return 1;
}
println!("Identities to include in subject");
println!("Subject identities");
let mut identities: Vec<CertificateIdentity> = Vec::new();
loop {
let identity: String = Input::with_theme(theme)
@ -123,7 +130,7 @@ fn newcsr(cli_args: &ArgMatches) -> i32 {
}
let l = l.ok();
if !l.as_ref().unwrap().verify(&identity) {
println!("ERROR: locator not signed by this identity.");
println!("ERROR: locator was not signed by this identity.");
return 1;
}
l
@ -135,7 +142,7 @@ fn newcsr(cli_args: &ArgMatches) -> i32 {
});
}
println!("Networks to include in subject (empty to end)");
println!("Subject networks (empty to end)");
let mut networks: Vec<CertificateNetwork> = Vec::new();
loop {
let nwid: String = Input::with_theme(theme)
@ -170,26 +177,7 @@ fn newcsr(cli_args: &ArgMatches) -> i32 {
})
}
println!("Certificates to reference in subject (empty to end)");
let mut certificates: Vec<CertificateSerialNo> = Vec::new();
loop {
let sn: String = Input::with_theme(theme)
.with_prompt(format!(" [{}] Certificate serial number (empty to end)", certificates.len() + 1))
.allow_empty(true)
.interact_text()
.unwrap_or_default();
if sn.is_empty() {
break;
}
let sn = CertificateSerialNo::new_from_string(sn.as_str());
if sn.is_err() {
println!("ERROR: invalid certificate serial number: {}", sn.err().unwrap().to_str());
return 1;
}
certificates.push(sn.ok().unwrap());
}
println!("URLs to check for updated certificates for this subject");
println!("Subject certificate update URLs");
let mut update_urls: Vec<String> = Vec::new();
loop {
let url: String = Input::with_theme(theme)
@ -208,7 +196,7 @@ fn newcsr(cli_args: &ArgMatches) -> i32 {
update_urls.push(url);
}
println!("Certificate \"name\" (same as X509 certificates, all fields optional)");
println!("Certificate name information (all fields are optional)");
let name = CertificateName {
serial_no: Input::with_theme(theme).with_prompt(" Serial").allow_empty(true).interact_text().unwrap_or_default(),
common_name: Input::with_theme(theme).with_prompt(" Common Name").allow_empty(true).interact_text().unwrap_or_default(),
@ -228,20 +216,30 @@ fn newcsr(cli_args: &ArgMatches) -> i32 {
timestamp,
identities,
networks,
certificates,
update_urls,
name,
unique_id: Vec::new(),
unique_id_proof_signature: Vec::new(),
unique_id_signature: Vec::new(),
};
subject.new_csr(subject_unique_id.as_ref()).map_or(1, |csr| {
let p = cli_args.value_of("path").unwrap();
std::fs::write(p, csr).map_or_else(|e| {
let (pubk, privk) = Certificate::new_key_pair(CertificatePublicKeyAlgorithm::ECDSANistP384).ok().unwrap();
subject.new_csr(pubk.as_ref(), subject_unique_id_private_key.as_ref().map(|k| k.as_ref())).map_or_else(|e| {
println!("ERROR: error creating CRL: {}", e.to_str());
1
}, |csr| {
let csr_path = cli_args.value_of("csrpath").unwrap();
std::fs::write(csr_path, csr).map_or_else(|e| {
println!("ERROR: unable to write CSR: {}", e.to_string());
1
}, |_| {
println!("CSR written to {}", p);
0
let secret_path = cli_args.value_of("secretpath").unwrap();
std::fs::write(secret_path, hex::encode(privk)).map_or_else(|e| {
let _ = std::fs::remove_file(csr_path);
println!("ERROR: unable to write secret: {}", e.to_string());
1
}, |_| {
println!("CSR written to '{}', certificate secret to '{}'", csr_path, secret_path);
0
})
})
})
}
@ -278,7 +276,7 @@ pub(crate) fn run(store: Arc<Store>, cli_args: &ArgMatches) -> i32 {
match cli_args.subcommand() {
("list", None) => list(&store),
("show", Some(sub_cli_args)) => show(&store, sub_cli_args),
("newsid", sub_cli_args) => newsid(sub_cli_args),
("newsuid", sub_cli_args) => newsuid(sub_cli_args),
("newcsr", Some(sub_cli_args)) => newcsr(sub_cli_args),
("sign", Some(sub_cli_args)) => sign(&store, sub_cli_args),
("verify", Some(sub_cli_args)) => verify(&store, sub_cli_args),

View file

@ -118,8 +118,8 @@ Advanced Operations:
cert <command> [args]
· list List certificates at local node
· show <serial> Show certificate details
newsid [sid secret out] Create a new subject unique ID
newcsr <csr output path> Create a subject CSR (interactive)
newsuid [suid secret out] Create a subject unique ID secret
newcsr <csr out> <secret out> Create a CSR (interactive)
sign <csr> <identity> [cert out] Sign a CSR to create a certificate
verify <cert> Verify certificate (not chain)
dump <cert> Verify and print certificate
@ -277,10 +277,11 @@ fn main() {
.subcommand(App::new("list"))
.subcommand(App::new("show")
.arg(Arg::with_name("serial").index(1).required(true)))
.subcommand(App::new("newsid")
.subcommand(App::new("newsuid")
.arg(Arg::with_name("path").index(1).required(false)))
.subcommand(App::new("newcsr")
.arg(Arg::with_name("path").index(1).required(true)))
.arg(Arg::with_name("csrpath").index(1).required(true))
.arg(Arg::with_name("secretpath").index(2).required(true)))
.subcommand(App::new("sign")
.arg(Arg::with_name("csr").index(1).required(true))
.arg(Arg::with_name("identity").index(2).required(true))