From 7b869684c6bd13a24091732141f102bb98b233c7 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 3 Jul 2020 14:42:29 -0700 Subject: [PATCH] More cert Go plumbing. --- pkg/zerotier/certificate.go | 130 +++++++++++++++++++++++++++++++++++- pkg/zerotier/locator.go | 24 +++++-- pkg/zerotier/misc.go | 3 + 3 files changed, 148 insertions(+), 9 deletions(-) diff --git a/pkg/zerotier/certificate.go b/pkg/zerotier/certificate.go index 97bbadc68..adc695c85 100644 --- a/pkg/zerotier/certificate.go +++ b/pkg/zerotier/certificate.go @@ -25,6 +25,7 @@ const ( 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 { SerialNo string `json:"serialNo,omitempty"` CommonName string `json:"commonName,omitempty"` @@ -40,16 +41,19 @@ type CertificateName struct { Host string `json:"host,omitempty"` } +// CertificateIdentity bundles an identity with an optional locator. type CertificateIdentity struct { Identity *Identity `json:"identity"` Locator *Locator `json:"locator,omitempty"` } +// CertificateNetwork bundles a network ID with the fingerprint of its primary controller. type CertificateNetwork struct { ID uint64 `json:"id"` - Controller *Fingerprint `json:"controller"` + Controller Fingerprint `json:"controller"` } +// CertificateSubject contains information about the subject of a certificate. type CertificateSubject struct { Timestamp int64 `json:"timestamp"` Identities []CertificateIdentity `json:"identities,omitempty"` @@ -61,6 +65,7 @@ type CertificateSubject struct { UniqueIDProofSignature []byte `json:"uniqueIdProofSignature,omitempty"` } +// Certificate is a Go reflection of the C ZT_Certificate struct. type Certificate struct { SerialNo []byte `json:"serialNo,omitempty"` Flags uint64 `json:"flags"` @@ -82,11 +87,130 @@ type cCertificate struct { 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 // 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 -// 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. func (c *Certificate) cCertificate() *cCertificate { var cc cCertificate diff --git a/pkg/zerotier/locator.go b/pkg/zerotier/locator.go index 5b7f02b30..8932bcbdf 100644 --- a/pkg/zerotier/locator.go +++ b/pkg/zerotier/locator.go @@ -30,6 +30,16 @@ type Locator struct { 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) { if ts <= 0 || len(endpoints) == 0 || signer == nil { return nil, ErrInvalidParameter @@ -46,7 +56,7 @@ func NewLocator(ts int64, endpoints []Endpoint, signer *Identity) (*Locator, err goloc := new(Locator) goloc.cl = unsafe.Pointer(loc) - return goloc, goloc.init() + return goloc, goloc.init(true) } func NewLocatorFromBytes(lb []byte) (*Locator, error) { @@ -60,7 +70,7 @@ func NewLocatorFromBytes(lb []byte) (*Locator, error) { goloc := new(Locator) goloc.cl = unsafe.Pointer(loc) - return goloc, goloc.init() + return goloc, goloc.init(true) } func NewLocatorFromString(s string) (*Locator, error) { @@ -76,7 +86,7 @@ func NewLocatorFromString(s string) (*Locator, error) { goloc := new(Locator) goloc.cl = unsafe.Pointer(loc) - return goloc, goloc.init() + return goloc, goloc.init(true) } func (loc *Locator) Validate(id *Identity) bool { @@ -116,7 +126,7 @@ func (loc *Locator) UnmarshalJSON(j []byte) error { return ErrInvalidParameter } loc.cl = cl - return loc.init() + return loc.init(true) } 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)) cfp := C.ZT_Locator_fingerprint(loc.cl) if uintptr(unsafe.Pointer(cfp)) == 0 { @@ -139,6 +149,8 @@ func (loc *Locator) init() error { } var buf [4096]byte 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 } diff --git a/pkg/zerotier/misc.go b/pkg/zerotier/misc.go index 7b41d94f5..f04669806 100644 --- a/pkg/zerotier/misc.go +++ b/pkg/zerotier/misc.go @@ -29,6 +29,9 @@ import ( // LogoChar is the unicode character that is ZeroTier's logo 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. var Base32StdLowerCase = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567").WithPadding(base32.NoPadding)