Go code cleanup and fixes, other cleanup.

This commit is contained in:
Adam Ierymenko 2020-01-20 16:03:19 -08:00
parent 5a29f9ba2d
commit 73b23f1b16
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
19 changed files with 210 additions and 405 deletions

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -45,8 +45,8 @@ Commands:
leave <network ID> Leave a virtual network
networks List joined VL2 virtual networks
network <network ID> Show verbose network info
addroot <identity> [IP/port] Add VL1 root with optional bootstrap IP
removeroot <identity|address> Remove VL1 root server
addroot <identity> [IP/port] Add root with optional bootstrap IP
removeroot <identity|address> Remove root
roots Show configured VL1 root servers
set <network ID> [option] [value] Get or set a network config option
manageips <boolean> Is IP management allowed?

View file

@ -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 {

View file

@ -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
}

View file

@ -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)
}

View file

@ -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) {

View file

@ -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<ZT_GoNode *>(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;
}

View file

@ -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

View file

@ -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)

View file

@ -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"}
}

View file

@ -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))

View file

@ -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"`
}

View file

@ -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
*

View file

@ -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];

View file

@ -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 ******************************************************/
};

View file

@ -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

View file

@ -889,6 +889,11 @@ uint64_t ZT_Node_address(ZT_Node *node)
return reinterpret_cast<ZeroTier::Node *>(node)->address();
}
const ZT_Identity *ZT_Node_identity(ZT_Node *node)
{
return (const ZT_Identity *)(&(reinterpret_cast<ZeroTier::Node *>(node)->identity()));
}
void ZT_Node_status(ZT_Node *node,ZT_NodeStatus *status)
{
try {