Adam Ierymenko 5 лет назад
Родитель
Сommit
7e341ed397
4 измененных файлов с 200 добавлено и 2 удалено
  1. 30 1
      core/Certificate.cpp
  2. 22 1
      core/zerotier.h
  3. 138 0
      pkg/zerotier/certificate.go
  4. 10 0
      pkg/zerotier/errors.go

+ 30 - 1
core/Certificate.cpp

@@ -629,6 +629,8 @@ int ZT_Certificate_sign(
 	void *signedCert,
 	int *signedCertSize)
 {
+	if (!cert)
+		return ZT_RESULT_ERROR_BAD_PARAMETER;
 	ZeroTier::Certificate c(*cert);
 	if (!c.sign(*reinterpret_cast<const ZeroTier::Identity *>(signer)))
 		return ZT_RESULT_ERROR_BAD_PARAMETER;
@@ -669,7 +671,34 @@ enum ZT_CertificateError ZT_Certificate_decode(
 	}
 }
 
-ZT_SDK_API void ZT_Certificate_delete(ZT_Certificate *cert)
+int ZT_Certificate_encode(
+	const ZT_Certificate *cert,
+	void *encoded,
+	int *encodedSize)
+{
+	if ((!cert) || (!encoded) || (!encodedSize))
+		return ZT_RESULT_ERROR_BAD_PARAMETER;
+	ZeroTier::Certificate c(*cert);
+	ZeroTier::Vector< uint8_t > enc(c.encode());
+	if ((int)enc.size() > *encodedSize)
+		return ZT_RESULT_ERROR_BAD_PARAMETER;
+	ZeroTier::Utils::copy(encoded, enc.data(), (unsigned int)enc.size());
+	*encodedSize = (int)enc.size();
+	return ZT_RESULT_OK;
+}
+
+enum ZT_CertificateError ZT_Certificate_verify(const ZT_Certificate *cert)
+{
+	try {
+		if (!cert)
+			return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
+		return ZeroTier::Certificate(*cert).verify();
+	} catch ( ... ) {
+		return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
+	}
+}
+
+void ZT_Certificate_delete(ZT_Certificate *cert)
 {
 	if (cert)
 		delete reinterpret_cast<ZeroTier::Certificate *>(cert);

+ 22 - 1
core/zerotier.h

@@ -2764,7 +2764,7 @@ ZT_SDK_API int ZT_Certificate_newCSR(
 	int *csrSize);
 
 /**
- * Sign a CSR to generate a complete certificate
+ * Sign a CSR to generate a complete certificate.
  *
  * @param cert Certificate to sign
  * @param signer Signer identity (must contain secret key)
@@ -2799,6 +2799,27 @@ ZT_SDK_API enum ZT_CertificateError ZT_Certificate_decode(
 	int certSize,
 	int verify);
 
+/**
+ * Encode a certificate
+ *
+ * @param cert Certificate to encode
+ * @param encoded Buffer to store certificate (suggested size: 16384)
+ * @param encodedSize Value/result: size of certificate encoding buffer
+ * @return OK (0) or error
+ */
+ZT_SDK_API int ZT_Certificate_encode(
+	const ZT_Certificate *cert,
+	void *encoded,
+	int *encodedSize);
+
+/**
+ * Verify certificate signatures and internal structure.
+ *
+ * @param cert Certificate to verify
+ * @return Certificate error or ZT_CERTIFICATE_ERROR_NONE if no errors found.
+ */
+ZT_SDK_API enum ZT_CertificateError ZT_Certificate_verify(const ZT_Certificate *cert);
+
 /**
  * Free a certificate created with ZT_Certificate_decode()
  *

+ 138 - 0
pkg/zerotier/certificate.go

@@ -17,12 +17,15 @@ package zerotier
 import "C"
 
 import (
+	"fmt"
 	"unsafe"
 )
 
 const (
 	CertificateSerialNoSize    = 48
 	CertificateMaxStringLength = int(C.ZT_CERTIFICATE_MAX_STRING_LENGTH)
+
+	CertificateUniqueIdTypeNistP384 = int(C.ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384)
 )
 
 // CertificateName identifies a real-world entity that owns a subject or has signed a certificate.
@@ -79,6 +82,8 @@ type Certificate struct {
 	Signature          []byte             `json:"signature,omitempty"`
 }
 
+// CCertificate wraps a pointer to a C ZT_Certificate with any related allocated memory.
+// Only the 'C' field should be used directly, and only this field is exported.
 type CCertificate struct {
 	C                             unsafe.Pointer
 	internalCertificate           C.ZT_Certificate
@@ -89,6 +94,65 @@ type CCertificate struct {
 	internalSubjectUpdateURLsData [][]byte
 }
 
+func certificateErrorToError(cerr int) error {
+	switch cerr {
+	case C.ZT_CERTIFICATE_ERROR_NONE:
+		return nil
+	case C.ZT_CERTIFICATE_ERROR_HAVE_NEWER_CERT:
+		return ErrCertificateHaveNewerCert
+	case C.ZT_CERTIFICATE_ERROR_INVALID_FORMAT:
+		return ErrCertificateInvalidFormat
+	case C.ZT_CERTIFICATE_ERROR_INVALID_IDENTITY:
+		return ErrCertificateInvalidIdentity
+	case C.ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE:
+		return ErrCertificateInvalidPrimarySignature
+	case C.ZT_CERTIFICATE_ERROR_INVALID_CHAIN:
+		return ErrCertificateInvalidChain
+	case C.ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE:
+		return ErrCertificateInvalidComponentSignature
+	case C.ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF:
+		return ErrCertificateInvalidUniqueIDProof
+	case C.ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS:
+		return ErrCertificateMissingRequiredFields
+	case C.ZT_CERTIFICATE_ERROR_OUT_OF_VALID_TIME_WINDOW:
+		return ErrCertificateOutOfValidTimeWindow
+	}
+	return ErrInternal
+}
+
+// NewCertificateFromBytes decodes a certificate from an encoded byte string.
+// Note that this is also used to decode a CSR. When used for a CSR only the
+// Subject part of the certificate will contain anything and the rest will be
+// blank. If 'verify' is true the certificate will also be verified. If using
+// to decode a CSR this should be false as a CSR will not contain a full set
+// of fields or a certificate signature.
+func NewCertificateFromBytes(cert []byte, verify bool) (*Certificate, error) {
+	if len(cert) == 0 {
+		return nil, ErrInvalidParameter
+	}
+	var dec unsafe.Pointer
+	ver := C.int(0)
+	if verify {
+		ver = 1
+	}
+	cerr := C.ZT_Certificate_decode((**C.ZT_Certificate)(unsafe.Pointer(&dec)), unsafe.Pointer(&cert[0]), C.int(len(cert)), ver)
+	if dec != unsafe.Pointer(nil) {
+		defer C.ZT_Certificate_delete((*C.ZT_Certificate)(dec))
+	}
+	if cerr != 0 {
+		return nil, certificateErrorToError(int(cerr))
+	}
+	if dec == unsafe.Pointer(nil) {
+		return nil, ErrInternal
+	}
+
+	goCert := NewCertificateFromCCertificate(dec)
+	if goCert == nil {
+		return nil, ErrInternal
+	}
+	return goCert, nil
+}
+
 // NewCertificateFromCCertificate translates a C ZT_Certificate into a Go Certificate.
 func NewCertificateFromCCertificate(ccptr unsafe.Pointer) *Certificate {
 	cc := (*C.ZT_Certificate)(ccptr)
@@ -349,3 +413,77 @@ func (c *Certificate) CCertificate() *CCertificate {
 
 	return &cc
 }
+
+// Marshal encodes this certificae as a byte array.
+func (c *Certificate) Marshal() ([]byte, error) {
+	cc := c.CCertificate()
+	if cc == nil {
+		return nil, ErrInternal
+	}
+	var encoded [16384]byte
+	encodedSize := C.int(16384)
+	rv := int(C.ZT_Certificate_encode((*C.ZT_Certificate)(cc.C), unsafe.Pointer(&encoded[0]), &encodedSize))
+	if rv != 0 {
+		return nil, fmt.Errorf("Certificate encode error %d", rv)
+	}
+	return append(make([]byte, 0, int(encodedSize)), encoded[0:int(encodedSize)]...), nil
+}
+
+// Verify returns nil on success or a certificate error if there is a problem with this certificate.
+func (c *Certificate) Verify() error {
+	cc := c.CCertificate()
+	if cc == nil {
+		return ErrInternal
+	}
+	return certificateErrorToError(int(C.ZT_Certificate_verify((*C.ZT_Certificate)(cc.C))))
+}
+
+// NewCertificateSubjectUniqueId creates a new certificate subject unique ID and corresponding private key.
+// Right now only one type is supported: CertificateUniqueIdTypeNistP384
+func NewCertificateSubjectUniqueId(uniqueIdType int) (id []byte, priv []byte, err error) {
+	if uniqueIdType != CertificateUniqueIdTypeNistP384 {
+		err = ErrInvalidParameter
+		return
+	}
+	id = make([]byte, int(C.ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE))
+	priv = make([]byte, int(C.ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE))
+	idSize := C.int(len(id))
+	idPrivateSize := C.int(len(priv))
+	if C.ZT_Certificate_newSubjectUniqueId((C.enum_ZT_CertificateUniqueIdType)(uniqueIdType), unsafe.Pointer(&id[0]), &idSize, unsafe.Pointer(&priv[0]), &idPrivateSize) != 0 {
+		id = nil
+		priv = nil
+		err = ErrInvalidParameter
+		return
+	}
+	if int(idSize) != len(id) || int(idPrivateSize) != len(priv) {
+		id = nil
+		priv = nil
+		err = ErrInvalidParameter
+		return
+	}
+	return
+}
+
+// NewCertificateCSR creates a new certificate signing request (CSR) from a certificate subject and optional unique ID.
+func NewCertificateCSR(subject *CertificateSubject, uniqueId []byte, uniqueIdPrivate []byte) ([]byte, error) {
+	var tmp Certificate
+	tmp.Subject = *subject
+	ctmp := tmp.CCertificate()
+	if ctmp == nil {
+		return nil, ErrInternal
+	}
+	ccert := (*C.ZT_Certificate)(ctmp.C)
+	var uid unsafe.Pointer
+	var uidp unsafe.Pointer
+	if len(uniqueId) > 0 && len(uniqueIdPrivate) > 0 {
+		uid = unsafe.Pointer(&uniqueId[0])
+		uidp = unsafe.Pointer(&uniqueIdPrivate[0])
+	}
+	var csr [16384]byte
+	csrSize := C.int(16384)
+	rv := int(C.ZT_Certificate_newCSR(&(ccert.subject), uid, C.int(len(uniqueId)), uidp, C.int(len(uniqueIdPrivate)), unsafe.Pointer(&csr[0]), &csrSize))
+	if rv != 0 {
+		return nil, fmt.Errorf("newCSR error %d", rv)
+	}
+	return append(make([]byte, 0, int(csrSize)), csr[0:int(csrSize)]...), nil
+}

+ 10 - 0
pkg/zerotier/errors.go

@@ -29,6 +29,16 @@ const (
 	ErrTapInitFailed            Err = "unable to create native Tap instance"
 	ErrUnrecognizedIdentityType Err = "unrecognized identity type"
 	ErrInvalidKey               Err = "invalid key data"
+
+	ErrCertificateHaveNewerCert             Err = "a newer certificate for this subject unique ID is already loaded"
+	ErrCertificateInvalidFormat             Err = "invalid certificate format"
+	ErrCertificateInvalidIdentity           Err = "invalid identity in certificate"
+	ErrCertificateInvalidPrimarySignature   Err = "invalid primary signature"
+	ErrCertificateInvalidChain              Err = "certificate chain verification failed"
+	ErrCertificateInvalidComponentSignature Err = "an internal component of this certificate has an invalid signature"
+	ErrCertificateInvalidUniqueIDProof      Err = "certificate subject unique ID proof signature verification failed"
+	ErrCertificateMissingRequiredFields     Err = "certificate is missing one or more required fields"
+	ErrCertificateOutOfValidTimeWindow      Err = "certificate is out of its valid time window"
 )
 
 // APIErr is returned by the JSON API when a call fails