Browse Source

More cert Go plumbing.

Adam Ierymenko 5 years ago
parent
commit
7b869684c6
3 changed files with 148 additions and 9 deletions
  1. 127 3
      pkg/zerotier/certificate.go
  2. 18 6
      pkg/zerotier/locator.go
  3. 3 0
      pkg/zerotier/misc.go

+ 127 - 3
pkg/zerotier/certificate.go

@@ -25,6 +25,7 @@ const (
 	CertificateMaxStringLength = int(C.ZT_CERTIFICATE_MAX_STRING_LENGTH)
 	CertificateMaxStringLength = int(C.ZT_CERTIFICATE_MAX_STRING_LENGTH)
 )
 )
 
 
+// CertificateName identifies a real-world entity that owns a subject or has signed a certificate.
 type CertificateName struct {
 type CertificateName struct {
 	SerialNo      string `json:"serialNo,omitempty"`
 	SerialNo      string `json:"serialNo,omitempty"`
 	CommonName    string `json:"commonName,omitempty"`
 	CommonName    string `json:"commonName,omitempty"`
@@ -40,16 +41,19 @@ type CertificateName struct {
 	Host          string `json:"host,omitempty"`
 	Host          string `json:"host,omitempty"`
 }
 }
 
 
+// CertificateIdentity bundles an identity with an optional locator.
 type CertificateIdentity struct {
 type CertificateIdentity struct {
 	Identity *Identity `json:"identity"`
 	Identity *Identity `json:"identity"`
 	Locator  *Locator  `json:"locator,omitempty"`
 	Locator  *Locator  `json:"locator,omitempty"`
 }
 }
 
 
+// CertificateNetwork bundles a network ID with the fingerprint of its primary controller.
 type CertificateNetwork struct {
 type CertificateNetwork struct {
 	ID         uint64       `json:"id"`
 	ID         uint64       `json:"id"`
-	Controller *Fingerprint `json:"controller"`
+	Controller Fingerprint  `json:"controller"`
 }
 }
 
 
+// CertificateSubject contains information about the subject of a certificate.
 type CertificateSubject struct {
 type CertificateSubject struct {
 	Timestamp              int64                           `json:"timestamp"`
 	Timestamp              int64                           `json:"timestamp"`
 	Identities             []CertificateIdentity           `json:"identities,omitempty"`
 	Identities             []CertificateIdentity           `json:"identities,omitempty"`
@@ -61,6 +65,7 @@ type CertificateSubject struct {
 	UniqueIDProofSignature []byte                          `json:"uniqueIdProofSignature,omitempty"`
 	UniqueIDProofSignature []byte                          `json:"uniqueIdProofSignature,omitempty"`
 }
 }
 
 
+// Certificate is a Go reflection of the C ZT_Certificate struct.
 type Certificate struct {
 type Certificate struct {
 	SerialNo           []byte             `json:"serialNo,omitempty"`
 	SerialNo           []byte             `json:"serialNo,omitempty"`
 	Flags              uint64             `json:"flags"`
 	Flags              uint64             `json:"flags"`
@@ -82,11 +87,130 @@ type cCertificate struct {
 	internalSubjectUpdateURLsData [][]byte
 	internalSubjectUpdateURLsData [][]byte
 }
 }
 
 
-// cCertificate creates a C ZT_Certificate structure
+// newCertificateFromCCertificate translates a C ZT_Certificate into a Go Certificate.
+func newCertificateFromCCertificate(cc *C.ZT_Certificate) *Certificate {
+	c := new(Certificate)
+
+	if cc == nil {
+		return c
+	}
+
+	c.SerialNo = C.GoBytes(unsafe.Pointer(&cc.serialNo[0]), 48)
+	if allZero(c.SerialNo) {
+		c.SerialNo = nil
+	}
+	c.Flags = uint64(cc.flags)
+	c.Timestamp = int64(cc.timestamp)
+	c.Validity[0] = int64(cc.validity[0])
+	c.Validity[1] = int64(cc.validity[1])
+
+	c.Subject.Timestamp = int64(cc.subject.timestamp)
+
+	for i := 0; i < int(cc.subject.identityCount); i++ {
+		cid := (*C.ZT_Certificate_Identity)(unsafe.Pointer(uintptr(unsafe.Pointer(cc.subject.identities)) + (uintptr(C.sizeof_ZT_Certificate_Identity) * uintptr(i))))
+		if cid.identity == nil {
+			return nil
+		}
+		id, err := newIdentityFromCIdentity(cid.identity)
+		if err != nil {
+			return nil
+		}
+		var loc *Locator
+		if cid.locator != nil {
+			loc, err = newLocatorFromCLocator(cid.locator)
+			if err != nil {
+				return nil
+			}
+		}
+		c.Subject.Identities = append(c.Subject.Identities, CertificateIdentity{
+			Identity: id,
+			Locator:  loc,
+		})
+	}
+
+	for i := 0; i < int(cc.subject.networkCount); i++ {
+		cn := (*C.ZT_Certificate_Network)(unsafe.Pointer(uintptr(unsafe.Pointer(cc.subject.networks)) + (uintptr(C.sizeof_ZT_Certificate_Network) * uintptr(i))))
+		fp := newFingerprintFromCFingerprint(&cn.controller)
+		if fp == nil {
+			return nil
+		}
+		c.Subject.Networks = append(c.Subject.Networks, CertificateNetwork{
+			ID: uint64(cn.id),
+			Controller: *fp,
+		})
+	}
+
+	for i := 0; i < int(cc.subject.certificateCount); i++ {
+		csn := (*[48]byte)(unsafe.Pointer(uintptr(unsafe.Pointer(cc.subject.certificates)) + (uintptr(i) * pointerSize)))
+		c.Subject.Certificates = append(c.Subject.Certificates, *csn)
+	}
+
+	for i := 0; i < int(cc.subject.updateURLCount); i++ {
+		curl := *((**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(cc.subject.updateURLs)) + (uintptr(i) * pointerSize))))
+		c.Subject.UpdateURLs = append(c.Subject.UpdateURLs, C.GoString(curl))
+	}
+
+	c.Subject.Name.SerialNo = C.GoString(&cc.subject.name.serialNo[0])
+	c.Subject.Name.CommonName = C.GoString(&cc.subject.name.commonName[0])
+	c.Subject.Name.Country = C.GoString(&cc.subject.name.country[0])
+	c.Subject.Name.Organization = C.GoString(&cc.subject.name.organization[0])
+	c.Subject.Name.Unit = C.GoString(&cc.subject.name.unit[0])
+	c.Subject.Name.Locality = C.GoString(&cc.subject.name.locality[0])
+	c.Subject.Name.Province = C.GoString(&cc.subject.name.province[0])
+	c.Subject.Name.StreetAddress = C.GoString(&cc.subject.name.streetAddress[0])
+	c.Subject.Name.PostalCode = C.GoString(&cc.subject.name.postalCode[0])
+	c.Subject.Name.Email = C.GoString(&cc.subject.name.email[0])
+	c.Subject.Name.URL = C.GoString(&cc.subject.name.url[0])
+	c.Subject.Name.Host = C.GoString(&cc.subject.name.host[0])
+
+	if cc.subject.uniqueIdSize > 0 {
+		c.Subject.UniqueID = C.GoBytes(unsafe.Pointer(cc.subject.uniqueId), C.int(cc.subject.uniqueIdSize))
+		if cc.subject.uniqueIdProofSignatureSize > 0 {
+			c.Subject.UniqueIDProofSignature = C.GoBytes(unsafe.Pointer(cc.subject.uniqueIdProofSignature), C.int(cc.subject.uniqueIdProofSignatureSize))
+		}
+	}
+
+	if cc.issuer != nil {
+		id, err := newIdentityFromCIdentity(cc.issuer)
+		if err != nil {
+			return nil
+		}
+		c.Issuer = id
+	}
+
+	c.IssuerName.SerialNo = C.GoString(&cc.issuerName.serialNo[0])
+	c.IssuerName.CommonName = C.GoString(&cc.issuerName.commonName[0])
+	c.IssuerName.Country = C.GoString(&cc.issuerName.country[0])
+	c.IssuerName.Organization = C.GoString(&cc.issuerName.organization[0])
+	c.IssuerName.Unit = C.GoString(&cc.issuerName.unit[0])
+	c.IssuerName.Locality = C.GoString(&cc.issuerName.locality[0])
+	c.IssuerName.Province = C.GoString(&cc.issuerName.province[0])
+	c.IssuerName.StreetAddress = C.GoString(&cc.issuerName.streetAddress[0])
+	c.IssuerName.PostalCode = C.GoString(&cc.issuerName.postalCode[0])
+	c.IssuerName.Email = C.GoString(&cc.issuerName.email[0])
+	c.IssuerName.URL = C.GoString(&cc.issuerName.url[0])
+	c.IssuerName.Host = C.GoString(&cc.issuerName.host[0])
+
+	if cc.extendedAttributesSize > 0 {
+		c.ExtendedAttributes = C.GoBytes(unsafe.Pointer(cc.extendedAttributes), C.int(cc.extendedAttributesSize))
+	}
+
+	c.MaxPathLength = uint(cc.maxPathLength)
+
+	if cc.signatureSize > 0 {
+		c.Signature = C.GoBytes(unsafe.Pointer(cc.signature), C.int(cc.signatureSize))
+	}
+
+	return c
+}
+
+// cCertificate creates a C ZT_Certificate structure from the content of a Certificate.
+// This will return nil if an error occurs, which would indicate an invalid C
+// structure or one with invalid values.
 // The returned Go structure bundles this with some objects that have
 // The returned Go structure bundles this with some objects that have
 // to be created to set their pointers in ZT_Certificate. It's easier to
 // to be created to set their pointers in ZT_Certificate. It's easier to
 // manage allocation of these in Go and bundle them so Go's GC will clean
 // manage allocation of these in Go and bundle them so Go's GC will clean
-// them up automatically when cCertificate is releaed. Only the 'C' field
+// them up automatically when cCertificate is released. Only the 'C' field
 // in cCertificate should be directly used.
 // in cCertificate should be directly used.
 func (c *Certificate) cCertificate() *cCertificate {
 func (c *Certificate) cCertificate() *cCertificate {
 	var cc cCertificate
 	var cc cCertificate

+ 18 - 6
pkg/zerotier/locator.go

@@ -30,6 +30,16 @@ type Locator struct {
 	cl          unsafe.Pointer
 	cl          unsafe.Pointer
 }
 }
 
 
+func newLocatorFromCLocator(cl unsafe.Pointer) (*Locator, error) {
+	loc := new(Locator)
+	loc.cl = cl
+	err := loc.init(false)
+	if err != nil {
+		return nil, err
+	}
+	return loc, nil
+}
+
 func NewLocator(ts int64, endpoints []Endpoint, signer *Identity) (*Locator, error) {
 func NewLocator(ts int64, endpoints []Endpoint, signer *Identity) (*Locator, error) {
 	if ts <= 0 || len(endpoints) == 0 || signer == nil {
 	if ts <= 0 || len(endpoints) == 0 || signer == nil {
 		return nil, ErrInvalidParameter
 		return nil, ErrInvalidParameter
@@ -46,7 +56,7 @@ func NewLocator(ts int64, endpoints []Endpoint, signer *Identity) (*Locator, err
 
 
 	goloc := new(Locator)
 	goloc := new(Locator)
 	goloc.cl = unsafe.Pointer(loc)
 	goloc.cl = unsafe.Pointer(loc)
-	return goloc, goloc.init()
+	return goloc, goloc.init(true)
 }
 }
 
 
 func NewLocatorFromBytes(lb []byte) (*Locator, error) {
 func NewLocatorFromBytes(lb []byte) (*Locator, error) {
@@ -60,7 +70,7 @@ func NewLocatorFromBytes(lb []byte) (*Locator, error) {
 
 
 	goloc := new(Locator)
 	goloc := new(Locator)
 	goloc.cl = unsafe.Pointer(loc)
 	goloc.cl = unsafe.Pointer(loc)
-	return goloc, goloc.init()
+	return goloc, goloc.init(true)
 }
 }
 
 
 func NewLocatorFromString(s string) (*Locator, error) {
 func NewLocatorFromString(s string) (*Locator, error) {
@@ -76,7 +86,7 @@ func NewLocatorFromString(s string) (*Locator, error) {
 
 
 	goloc := new(Locator)
 	goloc := new(Locator)
 	goloc.cl = unsafe.Pointer(loc)
 	goloc.cl = unsafe.Pointer(loc)
-	return goloc, goloc.init()
+	return goloc, goloc.init(true)
 }
 }
 
 
 func (loc *Locator) Validate(id *Identity) bool {
 func (loc *Locator) Validate(id *Identity) bool {
@@ -116,7 +126,7 @@ func (loc *Locator) UnmarshalJSON(j []byte) error {
 		return ErrInvalidParameter
 		return ErrInvalidParameter
 	}
 	}
 	loc.cl = cl
 	loc.cl = cl
-	return loc.init()
+	return loc.init(true)
 }
 }
 
 
 func locatorFinalizer(obj interface{}) {
 func locatorFinalizer(obj interface{}) {
@@ -125,7 +135,7 @@ func locatorFinalizer(obj interface{}) {
 	}
 	}
 }
 }
 
 
-func (loc *Locator) init() error {
+func (loc *Locator) init(requiresDeletion bool) error {
 	loc.Timestamp = int64(C.ZT_Locator_timestamp(loc.cl))
 	loc.Timestamp = int64(C.ZT_Locator_timestamp(loc.cl))
 	cfp := C.ZT_Locator_fingerprint(loc.cl)
 	cfp := C.ZT_Locator_fingerprint(loc.cl)
 	if uintptr(unsafe.Pointer(cfp)) == 0 {
 	if uintptr(unsafe.Pointer(cfp)) == 0 {
@@ -139,6 +149,8 @@ func (loc *Locator) init() error {
 	}
 	}
 	var buf [4096]byte
 	var buf [4096]byte
 	loc.String = C.GoString(C.ZT_Locator_toString(loc.cl, (*C.char)(unsafe.Pointer(&buf[0])), 4096))
 	loc.String = C.GoString(C.ZT_Locator_toString(loc.cl, (*C.char)(unsafe.Pointer(&buf[0])), 4096))
-	runtime.SetFinalizer(loc, locatorFinalizer)
+	if requiresDeletion {
+		runtime.SetFinalizer(loc, locatorFinalizer)
+	}
 	return nil
 	return nil
 }
 }

+ 3 - 0
pkg/zerotier/misc.go

@@ -29,6 +29,9 @@ import (
 // LogoChar is the unicode character that is ZeroTier's logo
 // LogoChar is the unicode character that is ZeroTier's logo
 const LogoChar = "⏁"
 const LogoChar = "⏁"
 
 
+// pointerSize is the size of a pointer on this system
+const pointerSize = unsafe.Sizeof(uintptr(0))
+
 // Base32StdLowerCase is a base32 encoder/decoder using a lower-case standard alphabet and no padding.
 // Base32StdLowerCase is a base32 encoder/decoder using a lower-case standard alphabet and no padding.
 var Base32StdLowerCase = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567").WithPadding(base32.NoPadding)
 var Base32StdLowerCase = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567").WithPadding(base32.NoPadding)