diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 61a48d077..053e85304 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -16,8 +16,8 @@ ZeroTier Release Notes * Changed default primary ZeroTier port to 893 (for new nodes) to exploit friendlier NAT behavior on ports numbered under 1024. Added some more aggressive NAT-t techniques that work with this and can often traverse symmetric NATs. * Added stubs for future support for alternative transports including HTTP/HTTPS, WebRTC, Web Sockets, and "naked" Ethernet (on LAN). * Improved packet assemble/decode performance by moving to a lock-free bounds-checking scheme for buffers and a shared memory buffer pool. + * AES encryption is now the default for communicating with 2.0+ nodes. * Added support for a new identity type with NIST P-384 curves for future FIPS compliance. Curve25519 is still the default. - * AES encryption is now the default for communicating with 2.0+ nodes. AES uses a GMAC-based "SIV" mode for improved resilience against nonce reuse, but constructed in a way that could be FIPS certified. * Compression is now only enabled for control packets as most data these days is encrypted or already compressed. This improves performance in almost all cases. * Minor API changes (for those who use the core directly) to support faster buffer handling, reduced memory copying, exposure of identity functions and full node identity, and improved state object load/store semantics. diff --git a/go/attic/endpoint.go b/go/attic/endpoint.go index 2ceab7d4c..f13473966 100644 --- a/go/attic/endpoint.go +++ b/go/attic/endpoint.go @@ -19,10 +19,10 @@ const ( // Endpoint wraps a variety of different ways of describing a node's physical network location. type Endpoint struct { // Type is this endpoint's type - Type int + Type int // Location is the X, Y, Z coordinate of this endpoint or 0,0,0 if unspecified. - Location [3]int + Location [3]int value, value2 interface{} } @@ -60,11 +60,11 @@ func (ep *Endpoint) unmarshalZT(b []byte) (int, error) { break } } - if stringEnd == 0 || (stringEnd + 2) > len(b) { + if stringEnd == 0 || (stringEnd+2) > len(b) { return 0, ErrInvalidEndpoint } ep.value = string(b[7:stringEnd]) - port := binary.BigEndian.Uint16(b[stringEnd:stringEnd+2]) + port := binary.BigEndian.Uint16(b[stringEnd : stringEnd+2]) ep.value2 = &port return stringEnd + 2, nil case EndpointTypeZeroTier: @@ -76,7 +76,7 @@ func (ep *Endpoint) unmarshalZT(b []byte) (int, error) { return 0, err } ep.value = a - ep.value2 = append(make([]byte,0,48),b[12:60]...) + ep.value2 = append(make([]byte, 0, 48), b[12:60]...) return 60, nil case EndpointTypeUrl: stringEnd := 0 diff --git a/go/attic/locator.go b/go/attic/locator.go index 3ea981fd4..989280c41 100644 --- a/go/attic/locator.go +++ b/go/attic/locator.go @@ -26,7 +26,7 @@ func (l Locator) Nil() bool { } // Endpoints obtains the endpoints described by this locator. -func (l Locator) Endpoints() (eps []Endpoint,err error) { +func (l Locator) Endpoints() (eps []Endpoint, err error) { if len(l) < 8 { err = ErrInvalidLocator return diff --git a/go/cmd/zerotier/cli/help.go b/go/cmd/zerotier/cli/help.go index c1925146e..d9e6e65b5 100644 --- a/go/cmd/zerotier/cli/help.go +++ b/go/cmd/zerotier/cli/help.go @@ -45,8 +45,8 @@ Commands: leave Leave a virtual network networks List joined VL2 virtual networks network Show verbose network info - addroot [IP/port] Add VL1 root with optional bootstrap IP - removeroot Remove VL1 root server + addroot [IP/port] Add root with optional bootstrap IP + removeroot Remove root roots Show configured VL1 root servers set [option] [value] Get or set a network config option manageips Is IP management allowed? diff --git a/go/cmd/zerotier/cli/misc.go b/go/cmd/zerotier/cli/misc.go index 467459928..a1f126e9e 100644 --- a/go/cmd/zerotier/cli/misc.go +++ b/go/cmd/zerotier/cli/misc.go @@ -24,12 +24,12 @@ import ( "zerotier/pkg/zerotier" ) -func apiGet(basePath, authToken, urlPath string, result interface{}) { - statusCode, err := zerotier.APIGet(basePath, zerotier.APISocketName, authToken, urlPath, result) +func apiGet(basePath, authToken, urlPath string, result interface{}) int64 { + statusCode, clock, err := zerotier.APIGet(basePath, zerotier.APISocketName, authToken, urlPath, result) if err != nil { fmt.Printf("FATAL: API response code %d: %s\n", statusCode, err.Error()) os.Exit(1) - return + return 0 } if statusCode != http.StatusOK { if statusCode == http.StatusUnauthorized { @@ -37,16 +37,17 @@ func apiGet(basePath, authToken, urlPath string, result interface{}) { } fmt.Printf("FATAL: API response code %d\n", statusCode) os.Exit(1) - return + return 0 } + return clock } -func apiPost(basePath, authToken, urlPath string, post, result interface{}) { - statusCode, err := zerotier.APIPost(basePath, zerotier.APISocketName, authToken, urlPath, post, result) +func apiPost(basePath, authToken, urlPath string, post, result interface{}) int64 { + statusCode, clock, err := zerotier.APIPost(basePath, zerotier.APISocketName, authToken, urlPath, post, result) if err != nil { fmt.Printf("FATAL: API response code %d: %s\n", statusCode, err.Error()) os.Exit(1) - return + return 0 } if statusCode != http.StatusOK { if statusCode == http.StatusUnauthorized { @@ -54,16 +55,17 @@ func apiPost(basePath, authToken, urlPath string, post, result interface{}) { } fmt.Printf("FATAL: API response code %d\n", statusCode) os.Exit(1) - return + return 0 } + return clock } -func apiDelete(basePath, authToken, urlPath string, result interface{}) { - statusCode, err := zerotier.APIDelete(basePath, zerotier.APISocketName, authToken, urlPath, result) +func apiDelete(basePath, authToken, urlPath string, result interface{}) int64 { + statusCode, clock, err := zerotier.APIDelete(basePath, zerotier.APISocketName, authToken, urlPath, result) if err != nil { fmt.Printf("FATAL: API response code %d: %s\n", statusCode, err.Error()) os.Exit(1) - return + return 0 } if statusCode != http.StatusOK { if statusCode == http.StatusUnauthorized { @@ -71,8 +73,9 @@ func apiDelete(basePath, authToken, urlPath string, result interface{}) { } fmt.Printf("FATAL: API response code %d\n", statusCode) os.Exit(1) - return + return 0 } + return clock } func enabledDisabled(f bool) string { diff --git a/go/cmd/zerotier/cli/peers.go b/go/cmd/zerotier/cli/peers.go index b417cb280..5aa4c8e98 100644 --- a/go/cmd/zerotier/cli/peers.go +++ b/go/cmd/zerotier/cli/peers.go @@ -23,7 +23,7 @@ import ( // Peers CLI command func Peers(basePath, authToken string, args []string, jsonOutput bool) { var peers []zerotier.Peer - apiGet(basePath, authToken, "/peer", &peers) + clock := apiGet(basePath, authToken, "/peer", &peers) if jsonOutput { fmt.Println(jsonDump(&peers)) @@ -36,7 +36,7 @@ func Peers(basePath, authToken string, args []string, jsonOutput bool) { address := "" if len(peer.Paths) > 0 { link = "DIRECT" - lastTX, lastRX = peer.Clock-peer.Paths[0].LastSend, peer.Clock-peer.Paths[0].LastReceive + lastTX, lastRX = clock-peer.Paths[0].LastSend, clock-peer.Paths[0].LastReceive if lastTX < 0 { lastTX = 0 } diff --git a/go/cmd/zerotier/cli/removeroot.go b/go/cmd/zerotier/cli/removeroot.go index 321f7057d..3573271b4 100644 --- a/go/cmd/zerotier/cli/removeroot.go +++ b/go/cmd/zerotier/cli/removeroot.go @@ -13,27 +13,6 @@ package cli -import ( - "fmt" - "net/http" - "net/url" - "os" - "strings" - - "zerotier/pkg/zerotier" -) - // RemoveRoot CLI command func RemoveRoot(basePath, authToken string, args []string) { - if len(args) != 1 { - Help() - os.Exit(1) - } - result, _ := zerotier.APIDelete(basePath, zerotier.APISocketName, authToken, "/root/"+url.PathEscape(strings.TrimSpace(args[0])), nil) - if result == http.StatusOK { - fmt.Printf("%s removed\n", args[0]) - os.Exit(0) - } - fmt.Printf("ERROR: root %s not found or another error occurred: status code %d\n", args[0], result) - os.Exit(1) } diff --git a/go/native/CoreTests.cpp b/go/native/CoreTests.cpp index db3efb237..add6f9c5b 100644 --- a/go/native/CoreTests.cpp +++ b/go/native/CoreTests.cpp @@ -154,65 +154,6 @@ extern "C" int ZT_TestCrypto() std::cout << "[crypto] getSecureRandom: " << Utils::hex(buf1,64,hexbuf) << ZT_EOL_S; } - { - std::cout << "[crypto] Testing and benchmarking AES-256..." ZT_EOL_S << " AES-256 (test vectors): "; std::cout.flush(); - { - AES tv(AES_TEST_VECTOR_0_KEY); - tv.encrypt(AES_TEST_VECTOR_0_IN,(uint8_t *)buf1); - if (memcmp(buf1,AES_TEST_VECTOR_0_OUT,16) != 0) { - std::cout << "FAILED (test vector 0 encrypt)" ZT_EOL_S; - return -1; - } - std::cout << "OK" ZT_EOL_S << " GMAC-AES-256 (test vectors): "; std::cout.flush(); - tv.init(AES_GMAC_VECTOR_0_KEY); - tv.gmac(AES_GMAC_VECTOR_0_IV,AES_GMAC_VECTOR_0_IN,sizeof(AES_GMAC_VECTOR_0_IN),(uint8_t *)buf2); - if (memcmp(buf2,AES_GMAC_VECTOR_0_OUT,16) != 0) { - std::cout << "FAILED (test vector 0) (" << Utils::hex(buf2,16,hexbuf) << ")" ZT_EOL_S; - return -1; - } - tv.init(AES_GMAC_VECTOR_1_KEY); - tv.gmac(AES_GMAC_VECTOR_1_IV,AES_GMAC_VECTOR_1_IN,sizeof(AES_GMAC_VECTOR_1_IN),(uint8_t *)buf2); - if (memcmp(buf2,AES_GMAC_VECTOR_1_OUT,16) != 0) { - std::cout << "FAILED (test vector 1) (" << Utils::hex(buf2,16,hexbuf) << ")" ZT_EOL_S; - return -1; - } - tv.init(AES_GMAC_VECTOR_2_KEY); - tv.gmac(AES_GMAC_VECTOR_2_IV,AES_GMAC_VECTOR_2_IN,sizeof(AES_GMAC_VECTOR_2_IN),(uint8_t *)buf2); - if (memcmp(buf2,AES_GMAC_VECTOR_2_OUT,16) != 0) { - std::cout << "FAILED (test vector 2) (" << Utils::hex(buf2,16,hexbuf) << ")" ZT_EOL_S; - return -1; - } - std::cout << "OK" ZT_EOL_S << " GMAC-AES-256 (benchmark): "; std::cout.flush(); - int64_t start = OSUtils::now(); - for(unsigned long i=0;i<500000;++i) { - tv.gmac((const uint8_t *)buf1,buf1,ZT_DEFAULT_MTU,(uint8_t *)buf1); - } - int64_t end = OSUtils::now(); - *dummy = hexbuf[0]; - std::cout << (((double)(500000 * ZT_DEFAULT_MTU) / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second (dummy: " << (unsigned int)*dummy << ")" ZT_EOL_S; - std::cout << " AES-256-CTR (benchmark): "; std::cout.flush(); - start = OSUtils::now(); - for(unsigned long i=0;i<1000000;++i) { - tv.ctr((const uint8_t *)hexbuf,buf1,ZT_DEFAULT_MTU,buf1); - *dummy = buf1[0]; - } - end = OSUtils::now(); - std::cout << (((1000000.0 * (double)ZT_DEFAULT_MTU) / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second (dummy: " << (unsigned int)*dummy << ")" ZT_EOL_S; - } - { - std::cout << " AES-256-GMAC-SIV (benchmark): "; std::cout.flush(); - AES k1,k2,k3,k4; - AES::initGmacCtrKeys(AES_TEST_VECTOR_0_KEY,k1,k2,k3,k4); - int64_t start = OSUtils::now(); - for(unsigned long i=0;i<500000;++i) { - AES::gmacSivEncrypt(k1,k2,k3,k4,(const uint8_t *)hexbuf,0,buf1,ZT_DEFAULT_MTU,buf1,(uint8_t *)(hexbuf + 8)); - *dummy = buf1[0]; - } - int64_t end = OSUtils::now(); - std::cout << (((double)(500000 * ZT_DEFAULT_MTU) / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second (dummy: " << (unsigned int)*dummy << ")" ZT_EOL_S; - } - } - { std::cout << "[crypto] Testing Salsa20... "; std::cout.flush(); for(unsigned int i=0;i<4;++i) { diff --git a/go/native/GoGlue.cpp b/go/native/GoGlue.cpp index 3c34c6a80..7b0046e6c 100644 --- a/go/native/GoGlue.cpp +++ b/go/native/GoGlue.cpp @@ -164,6 +164,7 @@ static void ZT_GoNode_StatePutFunction( len); } +static void _freeFunc(void *p) { if (p) free(p); } static int ZT_GoNode_StateGetFunction( ZT_Node *node, void *uptr, @@ -173,7 +174,7 @@ static int ZT_GoNode_StateGetFunction( void **data, void (**freeFunc)(void *)) { - *freeFunc = free; + *freeFunc = &_freeFunc; return goStateObjectGetFunc( reinterpret_cast(uptr)->goUserPtr, (int)objType, @@ -380,7 +381,9 @@ extern "C" void ZT_GoNode_delete(ZT_GoNode *gn) gn->backgroundTaskThread.join(); + gn->node->shutdown(nullptr); delete gn->node; + delete gn; } diff --git a/go/pkg/zerotier/api.go b/go/pkg/zerotier/api.go index 011fcc02d..4992da187 100644 --- a/go/pkg/zerotier/api.go +++ b/go/pkg/zerotier/api.go @@ -23,6 +23,7 @@ import ( "net/http" "path" "runtime" + "strconv" "strings" "time" @@ -35,76 +36,91 @@ const APISocketName = "apisocket" var startTime = TimeMs() // APIGet makes a query to the API via a Unix domain or windows pipe socket -func APIGet(basePath, socketName, authToken, queryPath string, obj interface{}) (int, error) { +func APIGet(basePath, socketName, authToken, queryPath string, obj interface{}) (int, int64, error) { client, err := createNamedSocketHTTPClient(basePath, socketName) if err != nil { - return http.StatusTeapot, err + return http.StatusTeapot, 0, err } req, err := http.NewRequest("GET", "http://socket"+queryPath, nil) if err != nil { - return http.StatusTeapot, err + return http.StatusTeapot, 0, err } req.Header.Add("Authorization", "bearer "+authToken) resp, err := client.Do(req) if err != nil { - return http.StatusTeapot, err + return http.StatusTeapot, 0, err } err = json.NewDecoder(resp.Body).Decode(obj) - return resp.StatusCode, err + ts := resp.Header.Get("X-ZT-Clock") + t := int64(0) + if len(ts) > 0 { + t, _ = strconv.ParseInt(ts, 10, 64) + } + return resp.StatusCode, t, err } // APIPost posts a JSON object to the API via a Unix domain or windows pipe socket and reads a response -func APIPost(basePath, socketName, authToken, queryPath string, post, result interface{}) (int, error) { +func APIPost(basePath, socketName, authToken, queryPath string, post, result interface{}) (int, int64, error) { client, err := createNamedSocketHTTPClient(basePath, socketName) if err != nil { - return http.StatusTeapot, err + return http.StatusTeapot, 0, err } var data []byte if post != nil { data, err = json.Marshal(post) if err != nil { - return http.StatusTeapot, err + return http.StatusTeapot, 0, err } } else { data = []byte("null") } req, err := http.NewRequest("POST", "http://socket"+queryPath, bytes.NewReader(data)) if err != nil { - return http.StatusTeapot, err + return http.StatusTeapot, 0, err } req.Header.Add("Content-Type", "application/json") req.Header.Add("Authorization", "bearer "+authToken) resp, err := client.Do(req) if err != nil { - return http.StatusTeapot, err + return http.StatusTeapot, 0, err + } + ts := resp.Header.Get("X-ZT-Clock") + t := int64(0) + if len(ts) > 0 { + t, _ = strconv.ParseInt(ts, 10, 64) } if result != nil { err = json.NewDecoder(resp.Body).Decode(result) - return resp.StatusCode, err + return resp.StatusCode, t, err } - return resp.StatusCode, nil + return resp.StatusCode, t, nil } // APIDelete posts DELETE to a path and fills result with the outcome (if any) if result is non-nil -func APIDelete(basePath, socketName, authToken, queryPath string, result interface{}) (int, error) { +func APIDelete(basePath, socketName, authToken, queryPath string, result interface{}) (int, int64, error) { client, err := createNamedSocketHTTPClient(basePath, socketName) if err != nil { - return http.StatusTeapot, err + return http.StatusTeapot, 0, err } req, err := http.NewRequest("DELETE", "http://socket"+queryPath, nil) if err != nil { - return http.StatusTeapot, err + return http.StatusTeapot, 0, err } req.Header.Add("Authorization", "bearer "+authToken) resp, err := client.Do(req) if err != nil { - return http.StatusTeapot, err + return http.StatusTeapot, 0, err + } + ts := resp.Header.Get("X-ZT-Clock") + t := int64(0) + if len(ts) > 0 { + t, _ = strconv.ParseInt(ts, 10, 64) } if result != nil { err = json.NewDecoder(resp.Body).Decode(result) - return resp.StatusCode, err + return resp.StatusCode, t, err } - return resp.StatusCode, nil + return resp.StatusCode, t, nil } // APIStatus is the object returned by API status inquiries @@ -164,7 +180,9 @@ func apiSetStandardHeaders(out http.ResponseWriter) { h.Set("Cache-Control", "no-cache, no-store, must-revalidate") h.Set("Expires", "0") h.Set("Pragma", "no-cache") - h.Set("Date", time.Now().UTC().Format(time.RFC1123)) + t := time.Now().UTC() + h.Set("Date", t.Format(time.RFC1123)) + h.Set("X-ZT-Clock", strconv.FormatInt(t.UnixNano() / int64(1000000), 10)) } func apiSendObj(out http.ResponseWriter, req *http.Request, httpStatusCode int, obj interface{}) error { @@ -342,7 +360,12 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e } } - if req.Method == http.MethodGet || req.Method == http.MethodHead { + if req.Method == http.MethodPost || req.Method == http.MethodPut { + if queriedID == 0 { + _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"peer not found"}) + return + } + } else if req.Method == http.MethodGet || req.Method == http.MethodHead { peers := node.Peers() if queriedID != 0 { for _, p := range peers { @@ -356,7 +379,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e _ = apiSendObj(out, req, http.StatusOK, peers) } } else { - out.Header().Set("Allow", "GET, HEAD") + out.Header().Set("Allow", "GET, HEAD, PUT, POST") _ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"peers are read only"}) } }) @@ -447,35 +470,6 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e //////////////////////////////////////////////////////////////////////////// - smux.HandleFunc("/root/", func(out http.ResponseWriter, req *http.Request) { - defer func() { - e := recover() - if e != nil { - _ = apiSendObj(out, req, http.StatusInternalServerError, &APIErr{"caught unexpected error in request handler"}) - } - }() - - if !apiCheckAuth(out, req, authToken) { - return - } - apiSetStandardHeaders(out) - - //var queriedName string - //if len(req.URL.Path) > 6 { - // queriedName = req.URL.Path[6:] - //} - - if req.Method == http.MethodDelete { - } else if req.Method == http.MethodPost || req.Method == http.MethodPut { - } else if req.Method == http.MethodGet || req.Method == http.MethodHead { - } else { - out.Header().Set("Allow", "GET, HEAD, PUT, POST, DELETE") - _ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"unsupported method: " + req.Method}) - } - }) - - //////////////////////////////////////////////////////////////////////////// - listener, err := createNamedSocketListener(basePath, APISocketName) if err != nil { return nil, nil, err diff --git a/go/pkg/zerotier/identity.go b/go/pkg/zerotier/identity.go index e46bd16d5..6cca4e162 100644 --- a/go/pkg/zerotier/identity.go +++ b/go/pkg/zerotier/identity.go @@ -20,6 +20,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "runtime" "strings" "unsafe" ) @@ -44,28 +45,42 @@ type Identity struct { idtype int publicKey []byte privateKey []byte + cid unsafe.Pointer +} + +func identityFinalizer(obj interface{}) { + id, _ := obj.(*Identity) + if id != nil && uintptr(id.cid) != 0 { + defer C.ZT_Identity_delete(id.cid) + } } func newIdentityFromCIdentity(cid unsafe.Pointer) (*Identity, error) { if uintptr(cid) == 0 { return nil, ErrInvalidParameter } + var idStrBuf [4096]byte idStr := C.ZT_Identity_toString(cid, (*C.char)(unsafe.Pointer(&idStrBuf[0])), 4096, 1) if uintptr(unsafe.Pointer(idStr)) == 0 { return nil, ErrInternal } - return NewIdentityFromString(C.GoString(idStr)) + + id, err := NewIdentityFromString(C.GoString(idStr)) + if err != nil { + return nil, err + } + + id.cid = cid + + runtime.SetFinalizer(id, identityFinalizer) + + return id, nil } // NewIdentity generates a new identity of the selected type func NewIdentity(identityType int) (*Identity, error) { - cid := C.ZT_Identity_new(C.enum_ZT_Identity_Type(identityType)) - if uintptr(unsafe.Pointer(cid)) == 0 { - return nil, ErrInternal - } - defer C.ZT_Identity_delete(cid) - return newIdentityFromCIdentity(cid) + return newIdentityFromCIdentity(C.ZT_Identity_new(C.enum_ZT_Identity_Type(identityType))) } // NewIdentityFromString generates a new identity from its string representation. @@ -77,7 +92,7 @@ func NewIdentityFromString(s string) (*Identity, error) { } var err error - var id Identity + id := new(Identity) id.address, err = NewAddressFromString(ss[0]) if err != nil { return nil, err @@ -125,7 +140,9 @@ func NewIdentityFromString(s string) (*Identity, error) { } - return &id, nil + runtime.SetFinalizer(id, identityFinalizer) + + return id, nil } // Address returns this identity's address @@ -167,32 +184,34 @@ func (id *Identity) String() string { // LocallyValidate performs local self-validation of this identity func (id *Identity) LocallyValidate() bool { - idCStr := C.CString(id.String()) - defer C.free(unsafe.Pointer(idCStr)) - cid := C.ZT_Identity_fromString(idCStr) - if uintptr(cid) == 0 { - return false + if uintptr(id.cid) == 0 { + idCStr := C.CString(id.String()) + defer C.free(unsafe.Pointer(idCStr)) + id.cid = C.ZT_Identity_fromString(idCStr) + if uintptr(id.cid) == 0 { + return false + } } - defer C.ZT_Identity_delete(cid) - return C.ZT_Identity_validate(cid) != 0 + return C.ZT_Identity_validate(id.cid) != 0 } // Sign signs a message with this identity func (id *Identity) Sign(msg []byte) ([]byte, error) { - idCStr := C.CString(id.PrivateKeyString()) - defer C.free(unsafe.Pointer(idCStr)) - cid := C.ZT_Identity_fromString(idCStr) - if uintptr(cid) == 0 { - return nil, ErrInvalidKey + if uintptr(id.cid) == 0 { + idCStr := C.CString(id.String()) + defer C.free(unsafe.Pointer(idCStr)) + id.cid = C.ZT_Identity_fromString(idCStr) + if uintptr(id.cid) == 0 { + return nil, ErrInvalidKey + } } - defer C.ZT_Identity_delete(cid) var dataP unsafe.Pointer if len(msg) > 0 { dataP = unsafe.Pointer(&msg[0]) } var sig [96]byte - sigLen := C.ZT_Identity_sign(cid, dataP, C.uint(len(msg)), unsafe.Pointer(&sig[0]), 96) + sigLen := C.ZT_Identity_sign(id.cid, dataP, C.uint(len(msg)), unsafe.Pointer(&sig[0]), 96) if sigLen <= 0 { return nil, ErrInvalidKey } @@ -206,19 +225,20 @@ func (id *Identity) Verify(msg, sig []byte) bool { return false } - idCStr := C.CString(id.String()) - defer C.free(unsafe.Pointer(idCStr)) - cid := C.ZT_Identity_fromString(idCStr) - if uintptr(cid) == 0 { - return false + if uintptr(id.cid) == 0 { + idCStr := C.CString(id.String()) + defer C.free(unsafe.Pointer(idCStr)) + id.cid = C.ZT_Identity_fromString(idCStr) + if uintptr(id.cid) == 0 { + return false + } } - defer C.ZT_Identity_delete(cid) var dataP unsafe.Pointer if len(msg) > 0 { dataP = unsafe.Pointer(&msg[0]) } - return C.ZT_Identity_verify(cid, dataP, C.uint(len(msg)), unsafe.Pointer(&sig[0]), C.uint(len(sig))) != 0 + return C.ZT_Identity_verify(id.cid, dataP, C.uint(len(msg)), unsafe.Pointer(&sig[0]), C.uint(len(sig))) != 0 } // MarshalJSON marshals this Identity in its string format (private key is never included) diff --git a/go/pkg/zerotier/localconfig.go b/go/pkg/zerotier/localconfig.go index a3f42f697..d06f526dc 100644 --- a/go/pkg/zerotier/localconfig.go +++ b/go/pkg/zerotier/localconfig.go @@ -89,7 +89,7 @@ type LocalConfig struct { } // Read this local config from a file, initializing to defaults if the file does not exist. -func (lc *LocalConfig) Read(p string, saveDefaultsIfNotExist bool,isTotallyNewNode bool) error { +func (lc *LocalConfig) Read(p string, saveDefaultsIfNotExist bool, isTotallyNewNode bool) error { if lc.Physical == nil { lc.Physical = make(map[string]LocalConfigPhysicalPathConfiguration) lc.Virtual = make(map[Address]LocalConfigVirtualAddressConfiguration) @@ -101,7 +101,7 @@ func (lc *LocalConfig) Read(p string, saveDefaultsIfNotExist bool,isTotallyNewNo } else { lc.Settings.PrimaryPort = 9993 } - lc.Settings.SecondaryPort = unassignedPrivilegedPorts[randomUInt() % uint(len(unassignedPrivilegedPorts))] + lc.Settings.SecondaryPort = unassignedPrivilegedPorts[randomUInt()%uint(len(unassignedPrivilegedPorts))] lc.Settings.PortSearch = true lc.Settings.PortMapping = true lc.Settings.LogSizeMax = 128 @@ -110,7 +110,7 @@ func (lc *LocalConfig) Read(p string, saveDefaultsIfNotExist bool,isTotallyNewNo case "windows": lc.Settings.InterfacePrefixBlacklist = []string{"loopback"} case "darwin": - lc.Settings.InterfacePrefixBlacklist = []string{"lo","utun","feth"} + lc.Settings.InterfacePrefixBlacklist = []string{"lo", "utun", "feth"} default: lc.Settings.InterfacePrefixBlacklist = []string{"lo"} } diff --git a/go/pkg/zerotier/node.go b/go/pkg/zerotier/node.go index 22b38bd1d..dce66320d 100644 --- a/go/pkg/zerotier/node.go +++ b/go/pkg/zerotier/node.go @@ -21,6 +21,7 @@ import "C" import ( "bytes" + "encoding/hex" "errors" "fmt" "io/ioutil" @@ -131,7 +132,7 @@ func NewNode(basePath string) (n *Node, err error) { } n.localConfigPath = path.Join(basePath, "local.conf") - _, identitySecretNotFoundErr := os.Stat(path.Join(basePath,"identity.secret")) + _, identitySecretNotFoundErr := os.Stat(path.Join(basePath, "identity.secret")) err = n.localConfig.Read(n.localConfigPath, true, identitySecretNotFoundErr != nil) if err != nil { return @@ -180,7 +181,7 @@ func NewNode(basePath string) (n *Node, err error) { } n.log.Printf("secondary port %d unavailable, trying a random port (port search enabled)", n.localConfig.Settings.SecondaryPort) if portCheckCount <= 64 { - n.localConfig.Settings.SecondaryPort = unassignedPrivilegedPorts[randomUInt() % uint(len(unassignedPrivilegedPorts))] + n.localConfig.Settings.SecondaryPort = unassignedPrivilegedPorts[randomUInt()%uint(len(unassignedPrivilegedPorts))] } else { n.localConfig.Settings.SecondaryPort = int(16384 + (randomUInt() % 16384)) } @@ -531,12 +532,15 @@ func (n *Node) Peers() []*Peer { p := (*C.ZT_Peer)(unsafe.Pointer(uintptr(unsafe.Pointer(pl.peers)) + (i * C.sizeof_ZT_Peer))) p2 := new(Peer) p2.Address = Address(p.address) + p2.Identity, _ = newIdentityFromCIdentity(unsafe.Pointer(p.identity)) + p2.IdentityHash = hex.EncodeToString((*[48]byte)(unsafe.Pointer(&p.identityHash[0]))[:]) p2.Version = [3]int{int(p.versionMajor), int(p.versionMinor), int(p.versionRev)} p2.Latency = int(p.latency) p2.Role = int(p.role) + p2.Bootstrap = NewInetAddressFromSockaddr(unsafe.Pointer(&p.bootstrap)) p2.Paths = make([]Path, 0, int(p.pathCount)) - for j := uintptr(0); j < uintptr(p.pathCount); j++ { + for j := 0; j < len(p2.Paths); j++ { pt := &p.paths[j] if pt.alive != 0 { a := sockaddrStorageToUDPAddr(&pt.address) @@ -552,7 +556,6 @@ func (n *Node) Peers() []*Peer { } } - p2.Clock = TimeMs() peers = append(peers, p2) } C.ZT_Node_freeQueryResult(unsafe.Pointer(n.zn), unsafe.Pointer(pl)) diff --git a/go/pkg/zerotier/peer.go b/go/pkg/zerotier/peer.go index c6cbf0cf0..ebce9c621 100644 --- a/go/pkg/zerotier/peer.go +++ b/go/pkg/zerotier/peer.go @@ -13,12 +13,22 @@ package zerotier +// Peer roles must be the same as in ZeroTierCore.h. + +// PeerRoleLeaf indicates a normal leaf node. +const PeerRoleLeaf = 0 + +// PeerRoleRoot indicates a root peer. +const PeerRoleRoot = 1 + // Peer is another ZeroTier node type Peer struct { - Address Address `json:"address"` - Version [3]int `json:"version"` - Latency int `json:"latency"` - Role int `json:"role"` - Paths []Path `json:"paths,omitempty"` - Clock int64 `json:"clock"` + Address Address `json:"address"` + Identity *Identity `json:"identity"` + IdentityHash string `json:"identityHash"` + Version [3]int `json:"version"` + Latency int `json:"latency"` + Role int `json:"role"` + Bootstrap *InetAddress `json:"bootstrap,omitempty"` + Paths []Path `json:"paths,omitempty"` } diff --git a/include/ZeroTierCore.h b/include/ZeroTierCore.h index e107ab7fa..24904aa2d 100644 --- a/include/ZeroTierCore.h +++ b/include/ZeroTierCore.h @@ -1016,8 +1016,8 @@ typedef struct */ enum ZT_PeerRole { - ZT_PEER_ROLE_LEAF = 0, // ordinary node - ZT_PEER_ROLE_ROOT = 1 // root server + ZT_PEER_ROLE_LEAF = 0, + ZT_PEER_ROLE_ROOT = 1 }; /** @@ -1612,6 +1612,17 @@ ZT_SDK_API enum ZT_ResultCode ZT_Node_removeRoot(ZT_Node *node,void *tptr,const */ ZT_SDK_API uint64_t ZT_Node_address(ZT_Node *node); +/** + * Get this node's identity + * + * The identity pointer returned by this function need not and should not be + * freed with ZT_Identity_delete(). It's valid until the node is deleted. + * + * @param node Node instance + * @return Identity + */ +ZT_SDK_API const ZT_Identity *ZT_Node_identity(ZT_Node *node); + /** * Get the status of this node * diff --git a/node/AES.cpp b/node/AES.cpp index a629cfc3f..f3b8ad571 100644 --- a/node/AES.cpp +++ b/node/AES.cpp @@ -616,8 +616,8 @@ void AES::_gmac_aesni(const uint8_t iv[12],const uint8_t *in,const unsigned int _mm_storeu_si128((__m128i *)out,_mm_xor_si128(y,t)); } -#define ZT_AES_CTR_AESNI_ROUND(kk) c0 = _mm_aesenc_si128(c0,kk); c1 = _mm_aesenc_si128(c1,kk); c2 = _mm_aesenc_si128(c2,kk); c3 = _mm_aesenc_si128(c3,kk); -void AES::_ctr_aesni(const __m128i key[14],const uint8_t iv[16],const uint8_t *in,unsigned int len,uint8_t *out) +#define ZT_AES_CTR_AESNI_ROUND(kk) c0 = _mm_aesenc_si128(c0,kk); c1 = _mm_aesenc_si128(c1,kk); c2 = _mm_aesenc_si128(c2,kk); c3 = _mm_aesenc_si128(c3,kk) +void AES::_ctr_aesni(const uint8_t iv[16],const uint8_t *in,unsigned int len,uint8_t *out) const { /* Because our CTR supports full 128-bit nonces, we must do a full 128-bit (big-endian) * increment to be compatible with canonical NIST-certified CTR implementations. That's @@ -641,11 +641,11 @@ void AES::_ctr_aesni(const __m128i key[14],const uint8_t iv[16],const uint8_t *i __m128i ctr3 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 3ULL),3LL)),swap128); ctr0 = _mm_shuffle_epi8(ctr0,swap128); - __m128i k0 = key[0]; - __m128i k1 = key[1]; + __m128i k0 = _k.ni.k[0]; + __m128i k1 = _k.ni.k[1]; while (len >= 64) { - __m128i ka = key[2]; + __m128i ka = _k.ni.k[2]; __m128i c0 = _mm_xor_si128(ctr0,k0); __m128i c1 = _mm_xor_si128(ctr1,k0); __m128i c2 = _mm_xor_si128(ctr2,k0); @@ -656,29 +656,29 @@ void AES::_ctr_aesni(const __m128i key[14],const uint8_t iv[16],const uint8_t *i ctr2 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 6ULL),6LL)),swap128); ctr3 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 7ULL),7LL)),swap128); ctr0 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 4ULL),4LL)),swap128); - __m128i kb = key[3]; + __m128i kb = _k.ni.k[3]; ZT_AES_CTR_AESNI_ROUND(k1); - __m128i kc = key[4]; + __m128i kc = _k.ni.k[4]; ZT_AES_CTR_AESNI_ROUND(ka); - __m128i kd = key[5]; + __m128i kd = _k.ni.k[5]; ZT_AES_CTR_AESNI_ROUND(kb); - ka = key[6]; + ka = _k.ni.k[6]; ZT_AES_CTR_AESNI_ROUND(kc); - kb = key[7]; + kb = _k.ni.k[7]; ZT_AES_CTR_AESNI_ROUND(kd); - kc = key[8]; + kc = _k.ni.k[8]; ZT_AES_CTR_AESNI_ROUND(ka); - kd = key[9]; + kd = _k.ni.k[9]; ZT_AES_CTR_AESNI_ROUND(kb); - ka = key[10]; + ka = _k.ni.k[10]; ZT_AES_CTR_AESNI_ROUND(kc); - kb = key[11]; + kb = _k.ni.k[11]; ZT_AES_CTR_AESNI_ROUND(kd); - kc = key[12]; + kc = _k.ni.k[12]; ZT_AES_CTR_AESNI_ROUND(ka); - kd = key[13]; + kd = _k.ni.k[13]; ZT_AES_CTR_AESNI_ROUND(kb); - ka = key[14]; + ka = _k.ni.k[14]; ZT_AES_CTR_AESNI_ROUND(kc); ZT_AES_CTR_AESNI_ROUND(kd); _mm_storeu_si128((__m128i *)out,_mm_xor_si128(_mm_loadu_si128((const __m128i *)in),_mm_aesenclast_si128(c0,ka))); @@ -690,12 +690,12 @@ void AES::_ctr_aesni(const __m128i key[14],const uint8_t iv[16],const uint8_t *i len -= 64; } - __m128i k2 = key[2]; - __m128i k3 = key[3]; - __m128i k4 = key[4]; - __m128i k5 = key[5]; - __m128i k6 = key[6]; - __m128i k7 = key[7]; + __m128i k2 = _k.ni.k[2]; + __m128i k3 = _k.ni.k[3]; + __m128i k4 = _k.ni.k[4]; + __m128i k5 = _k.ni.k[5]; + __m128i k6 = _k.ni.k[6]; + __m128i k7 = _k.ni.k[7]; while (len >= 16) { __m128i c0 = _mm_xor_si128(ctr0,k0); @@ -707,19 +707,19 @@ void AES::_ctr_aesni(const __m128i key[14],const uint8_t iv[16],const uint8_t *i c0 = _mm_aesenc_si128(c0,k4); c0 = _mm_aesenc_si128(c0,k5); c0 = _mm_aesenc_si128(c0,k6); - __m128i ka = key[8]; + __m128i ka = _k.ni.k[8]; c0 = _mm_aesenc_si128(c0,k7); - __m128i kb = key[9]; + __m128i kb = _k.ni.k[9]; c0 = _mm_aesenc_si128(c0,ka); - ka = key[10]; + ka = _k.ni.k[10]; c0 = _mm_aesenc_si128(c0,kb); - kb = key[11]; + kb = _k.ni.k[11]; c0 = _mm_aesenc_si128(c0,ka); - ka = key[12]; + ka = _k.ni.k[12]; c0 = _mm_aesenc_si128(c0,kb); - kb = key[13]; + kb = _k.ni.k[13]; c0 = _mm_aesenc_si128(c0,ka); - ka = key[14]; + ka = _k.ni.k[14]; c0 = _mm_aesenc_si128(c0,kb); _mm_storeu_si128((__m128i *)out,_mm_xor_si128(_mm_loadu_si128((const __m128i *)in),_mm_aesenclast_si128(c0,ka))); in += 16; @@ -729,25 +729,25 @@ void AES::_ctr_aesni(const __m128i key[14],const uint8_t iv[16],const uint8_t *i if (len) { __m128i c0 = _mm_xor_si128(ctr0,k0); - k0 = key[8]; + k0 = _k.ni.k[8]; c0 = _mm_aesenc_si128(c0,k1); c0 = _mm_aesenc_si128(c0,k2); - k1 = key[9]; + k1 = _k.ni.k[9]; c0 = _mm_aesenc_si128(c0,k3); c0 = _mm_aesenc_si128(c0,k4); - k2 = key[10]; + k2 = _k.ni.k[10]; c0 = _mm_aesenc_si128(c0,k5); c0 = _mm_aesenc_si128(c0,k6); - k3 = key[11]; + k3 = _k.ni.k[11]; c0 = _mm_aesenc_si128(c0,k7); c0 = _mm_aesenc_si128(c0,k0); - k0 = key[12]; + k0 = _k.ni.k[12]; c0 = _mm_aesenc_si128(c0,k1); c0 = _mm_aesenc_si128(c0,k2); - k1 = key[13]; + k1 = _k.ni.k[13]; c0 = _mm_aesenc_si128(c0,k3); c0 = _mm_aesenc_si128(c0,k0); - k2 = key[14]; + k2 = _k.ni.k[14]; c0 = _mm_aesenc_si128(c0,k1); c0 = _mm_aesenclast_si128(c0,k2); uint8_t tmp[16]; diff --git a/node/AES.hpp b/node/AES.hpp index 12f122e86..a38bcba54 100644 --- a/node/AES.hpp +++ b/node/AES.hpp @@ -106,177 +106,13 @@ public: { #ifdef ZT_AES_AESNI if (likely(Utils::CPUID.aes)) { - _ctr_aesni(_k.ni.k,iv,(const uint8_t *)in,len,(uint8_t *)out); + _ctr_aesni(iv,(const uint8_t *)in,len,(uint8_t *)out); return; } #endif _ctrSW(iv,in,len,out); } - /** - * Perform AES-GMAC-SIV encryption - * - * This is basically AES-CMAC-SIV but with GMAC in place of CMAC after - * GMAC is run through AES as a keyed hash to make it behave like a - * proper PRF. - * - * See: https://github.com/miscreant/meta/wiki/AES-SIV - * - * The advantage is that this can be described in terms of FIPS and NSA - * ceritifable primitives that are present in FIPS-compliant crypto - * modules. - * - * The extra AES-ECB (keyed hash) encryption of the AES-CTR IV prior - * to use makes the IV itself a secret. This is not strictly necessary - * but comes at little cost. - * - * This code is ZeroTier-specific in a few ways, like the way the IV - * is specified, but would not be hard to generalize. - * - * @param k1 GMAC key - * @param k2 GMAC auth tag keyed hash key - * @param k3 CTR IV keyed hash key - * @param k4 AES-CTR key - * @param iv 64-bit packet IV - * @param pc Packet characteristics byte - * @param in Message plaintext - * @param len Length of plaintext - * @param out Output buffer to receive ciphertext - * @param tag Output buffer to receive 64-bit authentication tag - */ - static inline void gmacSivEncrypt(const AES &k1,const AES &k2,const AES &k3,const AES &k4,const uint8_t iv[8],const uint8_t pc,const void *in,const unsigned int len,void *out,uint8_t tag[8]) - { -#ifdef __GNUC__ - uint8_t __attribute__ ((aligned (16))) miv[12]; - uint8_t __attribute__ ((aligned (16))) ctrIv[16]; -#else - uint8_t miv[12]; - uint8_t ctrIv[16]; -#endif - - // GMAC IV is 64-bit packet IV followed by other packet attributes to extend to 96 bits -#ifndef __GNUC__ - for(unsigned int i=0;i<8;++i) miv[i] = iv[i]; -#else - *((uint64_t *)miv) = *((const uint64_t *)iv); -#endif - miv[8] = pc; - miv[9] = (uint8_t)(len >> 16); - miv[10] = (uint8_t)(len >> 8); - miv[11] = (uint8_t)len; - - // Compute auth tag: AES-ECB[k2](GMAC[k1](miv,plaintext))[0:8] - k1.gmac(miv,in,len,ctrIv); - k2.encrypt(ctrIv,ctrIv); // ECB mode encrypt step is because GMAC is not a PRF -#ifdef ZT_NO_TYPE_PUNNING - for(unsigned int i=0;i<8;++i) tag[i] = ctrIv[i]; -#else - *((uint64_t *)tag) = *((uint64_t *)ctrIv); -#endif - - // Create synthetic CTR IV: AES-ECB[k3](TAG | MIV[0:4] | (MIV[4:8] XOR MIV[8:12])) -#ifndef __GNUC__ - for(unsigned int i=0;i<4;++i) ctrIv[i+8] = miv[i]; - for(unsigned int i=4;i<8;++i) ctrIv[i+8] = miv[i] ^ miv[i+4]; -#else - ((uint32_t *)ctrIv)[2] = ((const uint32_t *)miv)[0]; - ((uint32_t *)ctrIv)[3] = ((const uint32_t *)miv)[1] ^ ((const uint32_t *)miv)[2]; -#endif - k3.encrypt(ctrIv,ctrIv); - - // Encrypt with AES[k4]-CTR - k4.ctr(ctrIv,in,len,out); - } - - /** - * Decrypt a message encrypted with AES-GMAC-SIV and check its authenticity - * - * @param k1 GMAC key - * @param k2 GMAC auth tag keyed hash key - * @param k3 CTR IV keyed hash key - * @param k4 AES-CTR key - * @param iv 64-bit message IV - * @param pc Packet characteristics byte - * @param in Message ciphertext - * @param len Length of ciphertext - * @param out Output buffer to receive plaintext - * @param tag Authentication tag supplied with message - * @return True if authentication tags match and message appears authentic - */ - static inline bool gmacSivDecrypt(const AES &k1,const AES &k2,const AES &k3,const AES &k4,const uint8_t iv[8],const uint8_t pc,const void *in,const unsigned int len,void *out,const uint8_t tag[8]) - { -#ifdef __GNUC__ - uint8_t __attribute__ ((aligned (16))) miv[12]; - uint8_t __attribute__ ((aligned (16))) ctrIv[16]; - uint8_t __attribute__ ((aligned (16))) gmacOut[16]; -#else - uint8_t miv[12]; - uint8_t ctrIv[16]; - uint8_t gmacOut[16]; -#endif - - // Extend packet IV to 96-bit message IV using direction byte and message length -#ifdef ZT_NO_TYPE_PUNNING - for(unsigned int i=0;i<8;++i) miv[i] = iv[i]; -#else - *((uint64_t *)miv) = *((const uint64_t *)iv); -#endif - miv[8] = pc; - miv[9] = (uint8_t)(len >> 16); - miv[10] = (uint8_t)(len >> 8); - miv[11] = (uint8_t)len; - - // Recover synthetic and secret CTR IV from auth tag and packet IV -#ifndef __GNUC__ - for(unsigned int i=0;i<8;++i) ctrIv[i] = tag[i]; - for(unsigned int i=0;i<4;++i) ctrIv[i+8] = miv[i]; - for(unsigned int i=4;i<8;++i) ctrIv[i+8] = miv[i] ^ miv[i+4]; -#else - *((uint64_t *)ctrIv) = *((const uint64_t *)tag); - ((uint32_t *)ctrIv)[2] = ((const uint32_t *)miv)[0]; - ((uint32_t *)ctrIv)[3] = ((const uint32_t *)miv)[1] ^ ((const uint32_t *)miv)[2]; -#endif - k3.encrypt(ctrIv,ctrIv); - - // Decrypt with AES[k4]-CTR - k4.ctr(ctrIv,in,len,out); - - // Compute AES[k2](GMAC[k1](iv,plaintext)) - k1.gmac(miv,out,len,gmacOut); - k2.encrypt(gmacOut,gmacOut); - - // Check that packet's auth tag matches first 64 bits of AES(GMAC) -#ifdef ZT_NO_TYPE_PUNNING - return Utils::secureEq(gmacOut,tag,8); -#else - return (*((const uint64_t *)gmacOut) == *((const uint64_t *)tag)); -#endif - } - - /** - * Use KBKDF with HMAC-SHA-384 to derive four sub-keys for AES-GMAC-SIV from a single master key - * - * See section 5.1 at https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf - * - * @param masterKey Master 256-bit key - * @param k1 GMAC key - * @param k2 GMAC auth tag keyed hash key - * @param k3 CTR IV keyed hash key - * @param k4 AES-CTR key - */ - static inline void initGmacCtrKeys(const uint8_t masterKey[32],AES &k1,AES &k2,AES &k3,AES &k4) - { - uint8_t k[32]; - KBKDFHMACSHA384(masterKey,ZT_PROTO_KBKDF_LABEL_KEY_USE_AES_GMAC_SIV_K1,0,0,k); - k1.init(k); - KBKDFHMACSHA384(masterKey,ZT_PROTO_KBKDF_LABEL_KEY_USE_AES_GMAC_SIV_K2,0,0,k); - k2.init(k); - KBKDFHMACSHA384(masterKey,ZT_PROTO_KBKDF_LABEL_KEY_USE_AES_GMAC_SIV_K3,0,0,k); - k3.init(k); - KBKDFHMACSHA384(masterKey,ZT_PROTO_KBKDF_LABEL_KEY_USE_AES_GMAC_SIV_K4,0,0,k); - k4.init(k); - } - private: static const uint32_t Te0[256]; static const uint32_t Te1[256]; @@ -400,7 +236,7 @@ private: #ifdef ZT_AES_AESNI /********************************************************/ void _init_aesni(const uint8_t key[32]); - ZT_ALWAYS_INLINE void _encrypt_aesni(const void *in,void *out) const + ZT_ALWAYS_INLINE void _encrypt_aesni(const void *const in,void *const out) const { __m128i tmp; tmp = _mm_loadu_si128((const __m128i *)in); @@ -421,8 +257,8 @@ private: _mm_storeu_si128((__m128i *)out,_mm_aesenclast_si128(tmp,_k.ni.k[14])); } - void _gmac_aesni(const uint8_t iv[12],const uint8_t *in,const unsigned int len,uint8_t out[16]) const; - static void _ctr_aesni(const __m128i key[14],const uint8_t iv[16],const uint8_t *in,unsigned int len,uint8_t *out); + void _gmac_aesni(const uint8_t iv[12],const uint8_t *in,unsigned int len,uint8_t out[16]) const; + void _ctr_aesni(const uint8_t iv[16],const uint8_t *in,unsigned int len,uint8_t *out) const; #endif /* ZT_AES_AESNI ******************************************************/ }; diff --git a/node/Address.hpp b/node/Address.hpp index adeb14d2b..6d95aad0f 100644 --- a/node/Address.hpp +++ b/node/Address.hpp @@ -37,7 +37,7 @@ class Address { public: ZT_ALWAYS_INLINE Address() : _a(0) {} - explicit ZT_ALWAYS_INLINE Address(uint64_t a) : _a(a & 0xffffffffffULL) {} + explicit ZT_ALWAYS_INLINE Address(const uint64_t a) : _a(a & 0xffffffffffULL) {} /** * @param bits Raw address -- 5 bytes, big-endian byte order diff --git a/node/Node.cpp b/node/Node.cpp index ac05950f2..1d27dba88 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -889,6 +889,11 @@ uint64_t ZT_Node_address(ZT_Node *node) return reinterpret_cast(node)->address(); } +const ZT_Identity *ZT_Node_identity(ZT_Node *node) +{ + return (const ZT_Identity *)(&(reinterpret_cast(node)->identity())); +} + void ZT_Node_status(ZT_Node *node,ZT_NodeStatus *status) { try {