Even less bugs!

This commit is contained in:
Adam Ierymenko 2020-07-09 20:08:08 -07:00
parent 04d8c3dd79
commit f018fefeb4
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
7 changed files with 107 additions and 89 deletions

View file

@ -38,7 +38,7 @@ func TestCertificate() bool {
var c zerotier.Certificate var c zerotier.Certificate
c.SerialNo = make([]byte, 48) c.SerialNo = make([]byte, 48)
for i := 0; i < 48 ;i++ { for i := 0; i < 48; i++ {
c.SerialNo[i] = byte(i) c.SerialNo[i] = byte(i)
} }
c.Flags = 1234 c.Flags = 1234
@ -49,13 +49,13 @@ func TestCertificate() bool {
c.Subject.Timestamp = 31337 c.Subject.Timestamp = 31337
c.Subject.Identities = append(c.Subject.Identities, zerotier.CertificateIdentity{ c.Subject.Identities = append(c.Subject.Identities, zerotier.CertificateIdentity{
Identity: id, Identity: id,
Locator: nil, Locator: nil,
}) })
c.Subject.Networks = append(c.Subject.Networks, zerotier.CertificateNetwork{ c.Subject.Networks = append(c.Subject.Networks, zerotier.CertificateNetwork{
ID: 1111, ID: 1111,
Controller: zerotier.Fingerprint{ Controller: zerotier.Fingerprint{
Address: zerotier.Address(2222), Address: zerotier.Address(2222),
Hash: c.SerialNo, Hash: c.SerialNo,
}, },
}) })
c.Subject.Certificates = append(c.Subject.Certificates, c.SerialNo) c.Subject.Certificates = append(c.Subject.Certificates, c.SerialNo)
@ -115,25 +115,26 @@ func TestCertificate() bool {
} }
fmt.Printf("Checking certificate marshal/unmarshal... ") fmt.Printf("Checking certificate marshal/unmarshal... ")
cb, err := c.Marshal() for k := 0; k < 1024; k++ {
if err != nil { cb, err := c.Marshal()
fmt.Printf("marshal FAILED (%s)\n", err.Error()) if err != nil {
return false fmt.Printf("marshal FAILED (%s)\n", err.Error())
} return false
fmt.Printf("marshal: %d bytes ", len(cb)) }
c2, err = zerotier.NewCertificateFromBytes(cb, false) c2, err = zerotier.NewCertificateFromBytes(cb, false)
if err != nil { if err != nil {
fmt.Printf("unmarshal FAILED (%s)\n", err.Error()) fmt.Printf("unmarshal FAILED (%s)\n", err.Error())
return false return false
} }
cb2, err := c2.Marshal() cb2, err := c2.Marshal()
if err != nil { if err != nil {
fmt.Printf("second marshal FAILED (%s)\n", err.Error()) fmt.Printf("second marshal FAILED (%s)\n", err.Error())
return false return false
} }
if !bytes.Equal(cb, cb2) { if !bytes.Equal(cb, cb2) {
fmt.Printf("FAILED (results not equal)\n") fmt.Printf("FAILED (results not equal)\n")
return false return false
}
} }
fmt.Println("OK") fmt.Println("OK")
@ -143,7 +144,7 @@ func TestCertificate() bool {
fmt.Printf("CSR generate FAILED (%s)\n", err.Error()) fmt.Printf("CSR generate FAILED (%s)\n", err.Error())
return false return false
} }
fmt.Printf("CSR size: %d ",len(csr)) fmt.Printf("CSR size: %d ", len(csr))
csr2, err := zerotier.NewCertificateFromBytes(csr, false) csr2, err := zerotier.NewCertificateFromBytes(csr, false)
if err != nil { if err != nil {
fmt.Printf("CSR decode FAILED (%s)\n", err.Error()) fmt.Printf("CSR decode FAILED (%s)\n", err.Error())

View file

@ -307,19 +307,19 @@ bool Certificate::decode(const void *const data, const unsigned int len)
unsigned int cnt = (unsigned int)d.getUI("s.i$"); unsigned int cnt = (unsigned int)d.getUI("s.i$");
for (unsigned int i = 0; i < cnt; ++i) { for (unsigned int i = 0; i < cnt; ++i) {
const Vector< uint8_t > &identityData = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.i", i)]; const Vector< uint8_t > &identityData = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.i", i)];
const Vector< uint8_t > &locatorData = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.l", i)];
if (identityData.empty()) if (identityData.empty())
return false; return false;
Identity id; Identity id;
if (id.unmarshal(identityData.data(), (unsigned int)identityData.size()) <= 0) if (id.unmarshal(identityData.data(), (unsigned int)identityData.size()) <= 0)
return false; return false;
const Vector< uint8_t > &locatorData = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "s.i$.l", i)]; if (locatorData.empty()) {
if (!locatorData.empty()) { this->addSubjectIdentity(id);
} else {
Locator loc; Locator loc;
if (loc.unmarshal(locatorData.data(), (unsigned int)locatorData.size()) <= 0) if (loc.unmarshal(locatorData.data(), (unsigned int)locatorData.size()) <= 0)
return false; return false;
this->addSubjectIdentity(id, loc); this->addSubjectIdentity(id, loc);
} else {
this->addSubjectIdentity(id);
} }
} }
@ -463,10 +463,12 @@ ZT_CertificateError Certificate::verify() const
(this->subject.uniqueIdSize != ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE) || (this->subject.uniqueIdSize != ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE) ||
(this->subject.uniqueId[0] != ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384)) (this->subject.uniqueId[0] != ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384))
return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF; return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF;
Dictionary d; Dictionary d;
m_encodeSubject(this->subject, d, true); m_encodeSubject(this->subject, d, true);
Vector< uint8_t > enc; Vector< uint8_t > enc;
d.encode(enc); d.encode(enc);
uint8_t h[ZT_SHA384_DIGEST_SIZE]; uint8_t h[ZT_SHA384_DIGEST_SIZE];
SHA384(h, enc.data(), (unsigned int)enc.size()); SHA384(h, enc.data(), (unsigned int)enc.size());
static_assert(ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE == (ZT_ECC384_PUBLIC_KEY_SIZE + 1), "incorrect size"); static_assert(ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE == (ZT_ECC384_PUBLIC_KEY_SIZE + 1), "incorrect size");
@ -510,7 +512,7 @@ ZT_CertificateError Certificate::verify() const
Vector< uint8_t > Certificate::createCSR(const ZT_Certificate_Subject &s, const void *uniqueId, unsigned int uniqueIdSize, const void *uniqueIdPrivate, unsigned int uniqueIdPrivateSize) Vector< uint8_t > Certificate::createCSR(const ZT_Certificate_Subject &s, const void *uniqueId, unsigned int uniqueIdSize, const void *uniqueIdPrivate, unsigned int uniqueIdPrivateSize)
{ {
ZT_Certificate_Subject sc; ZT_Certificate_Subject sc;
Utils::copy< sizeof(ZT_Certificate_Subject) >(&sc, &s); Utils::copy< sizeof(ZT_Certificate_Subject) >(&sc,&s);
if ((uniqueId) && (uniqueIdSize > 0) && (uniqueIdPrivate) && (uniqueIdPrivateSize > 0)) { if ((uniqueId) && (uniqueIdSize > 0) && (uniqueIdPrivate) && (uniqueIdPrivateSize > 0)) {
sc.uniqueId = reinterpret_cast<const uint8_t *>(uniqueId); sc.uniqueId = reinterpret_cast<const uint8_t *>(uniqueId);

View file

@ -48,6 +48,33 @@ public:
~Dictionary(); ~Dictionary();
///*
ZT_INLINE void dump() const
{
printf("\n--\n");
for (const_iterator e(begin()); e != end(); ++e) {
printf("%.8x %s=", Utils::fnv1a32(e->second.data(), (unsigned int)e->second.size()), e->first.c_str());
bool binary = false;
for (Vector< uint8_t >::const_iterator c(e->second.begin()); c != e->second.end(); ++c) {
if ((*c < 33) || (*c > 126)) {
binary = true;
break;
}
}
if (binary) {
for (Vector< uint8_t >::const_iterator c(e->second.begin()); c != e->second.end(); ++c)
printf("%.2x", (unsigned int)*c);
} else {
Vector< uint8_t > s(e->second);
s.push_back(0);
printf("%s", s.data());
}
printf("\n");
}
printf("--\n");
}
//*/
/** /**
* Get a reference to a value * Get a reference to a value
* *

View file

@ -137,13 +137,13 @@ func NewCertificateFromBytes(cert []byte, verify bool) (*Certificate, error) {
ver = 1 ver = 1
} }
cerr := C.ZT_Certificate_decode((**C.ZT_Certificate)(unsafe.Pointer(&dec)), unsafe.Pointer(&cert[0]), C.int(len(cert)), ver) cerr := C.ZT_Certificate_decode((**C.ZT_Certificate)(unsafe.Pointer(&dec)), unsafe.Pointer(&cert[0]), C.int(len(cert)), ver)
defer C.ZT_Certificate_delete((*C.ZT_Certificate)(dec))
if cerr != 0 { if cerr != 0 {
return nil, certificateErrorToError(int(cerr)) return nil, certificateErrorToError(int(cerr))
} }
if dec == unsafe.Pointer(uintptr(0)) { if dec == nil {
return nil, ErrInternal return nil, ErrInternal
} }
defer C.ZT_Certificate_delete((*C.ZT_Certificate)(dec))
goCert := NewCertificateFromCCertificate(dec) goCert := NewCertificateFromCCertificate(dec)
if goCert == nil { if goCert == nil {
@ -299,10 +299,10 @@ func (c *Certificate) CCertificate() *CCertificate {
if len(c.Subject.Identities) > 0 { if len(c.Subject.Identities) > 0 {
subjectIdentities = make([]C.ZT_Certificate_Identity, len(c.Subject.Identities)) subjectIdentities = make([]C.ZT_Certificate_Identity, len(c.Subject.Identities))
for i, id := range c.Subject.Identities { for i, id := range c.Subject.Identities {
if id.Identity == nil || !id.Identity.initCIdentityPtr() { if id.Identity == nil {
return nil return nil
} }
subjectIdentities[i].identity = id.Identity.cid subjectIdentities[i].identity = id.Identity.cIdentity()
if id.Locator != nil { if id.Locator != nil {
subjectIdentities[i].locator = id.Locator.cl subjectIdentities[i].locator = id.Locator.cl
} }
@ -370,10 +370,7 @@ func (c *Certificate) CCertificate() *CCertificate {
} }
if c.Issuer != nil { if c.Issuer != nil {
if !c.Issuer.initCIdentityPtr() { cc.issuer = c.Issuer.cIdentity()
return nil
}
cc.issuer = c.Issuer.cid
} }
cStrCopy(unsafe.Pointer(&cc.issuerName.serialNo[0]), CertificateMaxStringLength+1, c.IssuerName.SerialNo) cStrCopy(unsafe.Pointer(&cc.issuerName.serialNo[0]), CertificateMaxStringLength+1, c.IssuerName.SerialNo)
@ -406,12 +403,14 @@ func (c *Certificate) CCertificate() *CCertificate {
// to throw away 'cc' and its components. // to throw away 'cc' and its components.
cc2 := &CCertificate{C: unsafe.Pointer(C._ZT_Certificate_clone2(C.uintptr_t(uintptr(unsafe.Pointer(&cc)))))} cc2 := &CCertificate{C: unsafe.Pointer(C._ZT_Certificate_clone2(C.uintptr_t(uintptr(unsafe.Pointer(&cc)))))}
runtime.SetFinalizer(cc2, func(obj interface{}) { runtime.SetFinalizer(cc2, func(obj interface{}) {
C.ZT_Certificate_delete((*C.ZT_Certificate)(obj.(*CCertificate).C)) if obj != nil {
C.ZT_Certificate_delete((*C.ZT_Certificate)(obj.(*CCertificate).C))
}
}) })
return cc2 return cc2
} }
// Marshal encodes this certificae as a byte array. // Marshal encodes this certificate as a byte array.
func (c *Certificate) Marshal() ([]byte, error) { func (c *Certificate) Marshal() ([]byte, error) {
cc := c.CCertificate() cc := c.CCertificate()
if cc == nil { if cc == nil {
@ -431,7 +430,7 @@ func (c *Certificate) Marshal() ([]byte, error) {
// parts of this Certificate, if any, are ignored. A new Certificate is returned with a completed // parts of this Certificate, if any, are ignored. A new Certificate is returned with a completed
// signature. // signature.
func (c *Certificate) Sign(id *Identity) (*Certificate, error) { func (c *Certificate) Sign(id *Identity) (*Certificate, error) {
if id == nil || !id.HasPrivate() || !id.initCIdentityPtr() { if id == nil || !id.HasPrivate() {
return nil, ErrInvalidParameter return nil, ErrInvalidParameter
} }
ctmp := c.CCertificate() ctmp := c.CCertificate()
@ -440,7 +439,7 @@ func (c *Certificate) Sign(id *Identity) (*Certificate, error) {
} }
var signedCert [16384]byte var signedCert [16384]byte
signedCertSize := C.int(16384) signedCertSize := C.int(16384)
rv := int(C.ZT_Certificate_sign((*C.ZT_Certificate)(ctmp.C), id.cid, unsafe.Pointer(&signedCert[0]), &signedCertSize)) rv := int(C.ZT_Certificate_sign((*C.ZT_Certificate)(ctmp.C), id.cIdentity(), unsafe.Pointer(&signedCert[0]), &signedCertSize))
if rv != 0 { if rv != 0 {
return nil, fmt.Errorf("signing failed: error %d", rv) return nil, fmt.Errorf("signing failed: error %d", rv)
} }

View file

@ -43,18 +43,18 @@ type Identity struct {
idtype int idtype int
publicKey []byte publicKey []byte
privateKey []byte privateKey []byte
cid unsafe.Pointer // ZT_Identity cid unsafe.Pointer
} }
func identityFinalizer(obj interface{}) { func identityFinalizer(obj interface{}) {
id, _ := obj.(*Identity) cid := obj.(*Identity).cid
if id != nil && uintptr(id.cid) != 0 { if cid != nil {
C.ZT_Identity_delete(id.cid) C.ZT_Identity_delete(cid)
} }
} }
func newIdentityFromCIdentity(cid unsafe.Pointer) (*Identity, error) { func newIdentityFromCIdentity(cid unsafe.Pointer) (*Identity, error) {
if uintptr(cid) == 0 { if cid == nil {
return nil, ErrInvalidParameter return nil, ErrInvalidParameter
} }
@ -69,39 +69,44 @@ func newIdentityFromCIdentity(cid unsafe.Pointer) (*Identity, error) {
return nil, err return nil, err
} }
id.cid = cid
runtime.SetFinalizer(id, identityFinalizer) runtime.SetFinalizer(id, identityFinalizer)
return id, nil return id, nil
} }
// initCIdentityPtr returns a pointer to the core ZT_Identity instance or nil/0 on error. // initCIdentityPtr returns a pointer to the core ZT_Identity instance or nil/0 on error.
func (id *Identity) initCIdentityPtr() bool { func (id *Identity) cIdentity() unsafe.Pointer {
if uintptr(id.cid) == 0 { if id.cid == nil {
str := id.PrivateKeyString() str := []byte(id.PrivateKeyString())
if len(str) == 0 { if len(str) == 0 {
str = id.String() str = []byte(id.String())
} }
idCStr := C.CString(str) if len(str) == 0 {
defer C.free(unsafe.Pointer(idCStr)) return nil
id.cid = C.ZT_Identity_fromString(idCStr)
if uintptr(id.cid) == 0 {
return false
} }
runtime.SetFinalizer(id, identityFinalizer) str = append(str, byte(0))
id.cid = C.ZT_Identity_fromString((*C.char)(unsafe.Pointer(&str[0])))
} }
return true return id.cid
} }
// NewIdentity generates a new identity of the selected type. // NewIdentity generates a new identity of the selected type.
func NewIdentity(identityType int) (*Identity, error) { func NewIdentity(identityType int) (*Identity, error) {
var cid unsafe.Pointer
switch identityType { switch identityType {
case C.ZT_IDENTITY_TYPE_C25519: case C.ZT_IDENTITY_TYPE_C25519:
return newIdentityFromCIdentity(C.ZT_Identity_new(C.ZT_IDENTITY_TYPE_C25519)) cid = C.ZT_Identity_new(C.ZT_IDENTITY_TYPE_C25519)
case C.ZT_IDENTITY_TYPE_P384: case C.ZT_IDENTITY_TYPE_P384:
return newIdentityFromCIdentity(C.ZT_Identity_new(C.ZT_IDENTITY_TYPE_P384)) cid = C.ZT_Identity_new(C.ZT_IDENTITY_TYPE_P384)
default:
return nil, ErrInvalidParameter
} }
return nil, ErrInvalidParameter id, err := newIdentityFromCIdentity(cid)
if err != nil {
return nil, err
}
id.cid = cid
return id, nil
} }
// NewIdentityFromString generates a new identity from its string representation. // NewIdentityFromString generates a new identity from its string representation.
@ -172,8 +177,7 @@ func (id *Identity) HasPrivate() bool { return len(id.privateKey) > 0 }
// Fingerprint gets this identity's address plus hash of public key(s). // Fingerprint gets this identity's address plus hash of public key(s).
func (id *Identity) Fingerprint() *Fingerprint { func (id *Identity) Fingerprint() *Fingerprint {
id.initCIdentityPtr() return newFingerprintFromCFingerprint(C.ZT_Identity_fingerprint(id.cIdentity()))
return newFingerprintFromCFingerprint(C.ZT_Identity_fingerprint(id.cid))
} }
// PrivateKeyString returns the full identity.secret if the private key is set, // PrivateKeyString returns the full identity.secret if the private key is set,
@ -210,24 +214,17 @@ func (id *Identity) String() string {
// LocallyValidate performs local self-validation of this identity // LocallyValidate performs local self-validation of this identity
func (id *Identity) LocallyValidate() bool { func (id *Identity) LocallyValidate() bool {
if !id.initCIdentityPtr() { return C.ZT_Identity_validate(id.cIdentity()) != 0
return false
}
return C.ZT_Identity_validate(id.cid) != 0
} }
// Sign signs a message with this identity // Sign signs a message with this identity
func (id *Identity) Sign(msg []byte) ([]byte, error) { func (id *Identity) Sign(msg []byte) ([]byte, error) {
if !id.initCIdentityPtr() {
return nil, ErrInvalidKey
}
var dataP unsafe.Pointer var dataP unsafe.Pointer
if len(msg) > 0 { if len(msg) > 0 {
dataP = unsafe.Pointer(&msg[0]) dataP = unsafe.Pointer(&msg[0])
} }
var sig [96]byte var sig [96]byte
sigLen := C.ZT_Identity_sign(id.cid, dataP, C.uint(len(msg)), unsafe.Pointer(&sig[0]), 96) sigLen := C.ZT_Identity_sign(id.cIdentity(), dataP, C.uint(len(msg)), unsafe.Pointer(&sig[0]), 96)
if sigLen <= 0 { if sigLen <= 0 {
return nil, ErrInvalidKey return nil, ErrInvalidKey
} }
@ -237,14 +234,14 @@ func (id *Identity) Sign(msg []byte) ([]byte, error) {
// Verify verifies a signature // Verify verifies a signature
func (id *Identity) Verify(msg, sig []byte) bool { func (id *Identity) Verify(msg, sig []byte) bool {
if len(sig) == 0 || !id.initCIdentityPtr() { if len(sig) == 0 {
return false return false
} }
var dataP unsafe.Pointer var dataP unsafe.Pointer
if len(msg) > 0 { if len(msg) > 0 {
dataP = unsafe.Pointer(&msg[0]) dataP = unsafe.Pointer(&msg[0])
} }
return C.ZT_Identity_verify(id.cid, dataP, C.uint(len(msg)), unsafe.Pointer(&sig[0]), C.uint(len(sig))) != 0 return C.ZT_Identity_verify(id.cIdentity(), dataP, C.uint(len(msg)), unsafe.Pointer(&sig[0]), C.uint(len(sig))) != 0
} }
// Equals performs a deep equality test between this and another identity // Equals performs a deep equality test between this and another identity

View file

@ -30,10 +30,10 @@ type Locator struct {
cl unsafe.Pointer cl unsafe.Pointer
} }
func newLocatorFromCLocator(cl unsafe.Pointer, requiresDeletion bool) (*Locator, error) { func newLocatorFromCLocator(cl unsafe.Pointer, needFinalizer bool) (*Locator, error) {
loc := new(Locator) loc := new(Locator)
loc.cl = cl loc.cl = cl
err := loc.init(requiresDeletion) err := loc.init(needFinalizer)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -48,8 +48,7 @@ func NewLocator(ts int64, endpoints []Endpoint, signer *Identity) (*Locator, err
for _, e := range endpoints { for _, e := range endpoints {
eps = append(eps, e.cep) eps = append(eps, e.cep)
} }
signer.initCIdentityPtr() loc := C.ZT_Locator_create(C.int64_t(ts), &eps[0], C.uint(len(eps)), signer.cIdentity())
loc := C.ZT_Locator_create(C.int64_t(ts), &eps[0], C.uint(len(eps)), signer.cid)
if uintptr(loc) == 0 { if uintptr(loc) == 0 {
return nil, ErrInvalidParameter return nil, ErrInvalidParameter
} }
@ -93,8 +92,7 @@ func (loc *Locator) Validate(id *Identity) bool {
if id == nil { if id == nil {
return false return false
} }
id.initCIdentityPtr() return C.ZT_Locator_verify(loc.cl, id.cIdentity()) != 0
return C.ZT_Locator_verify(loc.cl, id.cid) != 0
} }
func (loc *Locator) Bytes() []byte { func (loc *Locator) Bytes() []byte {
@ -135,7 +133,7 @@ func locatorFinalizer(obj interface{}) {
} }
} }
func (loc *Locator) init(requiresDeletion bool) error { func (loc *Locator) init(needFinalizer 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 {
@ -149,7 +147,7 @@ func (loc *Locator) init(requiresDeletion bool) 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))
if requiresDeletion { if needFinalizer {
runtime.SetFinalizer(loc, locatorFinalizer) runtime.SetFinalizer(loc, locatorFinalizer)
} }
return nil return nil

View file

@ -449,10 +449,7 @@ func (n *Node) Leave(nwid NetworkID) error {
// AddRoot designates a peer as root, adding it if missing. // AddRoot designates a peer as root, adding it if missing.
func (n *Node) AddRoot(id *Identity) (*Peer, error) { func (n *Node) AddRoot(id *Identity) (*Peer, error) {
if !id.initCIdentityPtr() { rc := C.ZT_Node_addRoot(n.zn, nil, id.cIdentity())
return nil, ErrInvalidKey
}
rc := C.ZT_Node_addRoot(n.zn, nil, id.cid)
if rc != 0 { if rc != 0 {
return nil, ErrInvalidParameter return nil, ErrInvalidParameter
} }
@ -537,10 +534,7 @@ func (n *Node) AddPeer(id *Identity) error {
if id == nil { if id == nil {
return ErrInvalidParameter return ErrInvalidParameter
} }
if !id.initCIdentityPtr() { rc := C.ZT_Node_addPeer(n.zn, nil, id.cIdentity())
return ErrInvalidKey
}
rc := C.ZT_Node_addPeer(n.zn, nil, id.cid)
if rc != 0 { if rc != 0 {
return ErrInvalidParameter return ErrInvalidParameter
} }