Certificate plumbing in Go.

This commit is contained in:
Adam Ierymenko 2020-07-27 16:45:43 -07:00
parent 825b19aedc
commit 81530e5990
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
16 changed files with 202 additions and 100 deletions

View file

@ -222,7 +222,7 @@ if(NOT PACKAGE_STATIC)
CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64" CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64"
) )
message("++ Adding flags for processor ${CMAKE_SYSTEM_PROCESSOR}") message("++ Adding flags for processor ${CMAKE_SYSTEM_PROCESSOR}")
add_compile_options(-maes -mrdrnd -mpclmul -msse -msse2 -mssse3) add_compile_options(-maes -mrdrnd -mpclmul -msse -msse2 -mssse3 -msse4)
endif() endif()
set(GO_BUILD_TAGS) set(GO_BUILD_TAGS)

View file

@ -13,5 +13,101 @@
package cli package cli
func Cert(basePath, authToken string, args []string, jsonOutput bool) { import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"zerotier/pkg/zerotier"
)
func Cert(basePath, authToken string, args []string, jsonOutput bool) int {
if len(args) < 1 {
Help()
return 1
}
switch args[0] {
case "newsid":
if len(args) > 2 {
Help()
return 1
}
uniqueId, uniqueIdPrivate, err := zerotier.NewCertificateSubjectUniqueId(zerotier.CertificateUniqueIdTypeNistP384)
if err != nil {
fmt.Printf("ERROR: unable to create unique ID and private key: %s\n", err.Error())
return 1
}
sec, err := json.MarshalIndent(&zerotier.CertificateSubjectUniqueIDSecret{UniqueID: uniqueId, UniqueIDSecret: uniqueIdPrivate}, "", " ")
if err != nil {
fmt.Printf("ERROR: unable to create unique ID and private key: %s\n", err.Error())
return 1
}
if len(args) == 1 {
fmt.Println(string(sec))
} else {
_ = ioutil.WriteFile(args[1], sec, 0600)
}
case "newcsr":
if len(args) < 3 {
Help()
return 1
}
var cs zerotier.CertificateSubject
csb, err := ioutil.ReadFile(args[1])
if err != nil {
fmt.Printf("ERROR: unable to read subject from %s: %s\n", args[1], err.Error())
return 1
}
err = json.Unmarshal(csb, &cs)
if err != nil {
fmt.Printf("ERROR: unable to read subject from %s: %s\n", args[1], err.Error())
return 1
}
var subj zerotier.CertificateSubjectUniqueIDSecret
subjb, err := ioutil.ReadFile(args[2])
if err != nil {
fmt.Printf("ERROR: unable to read unique ID secret from %s: %s\n", args[2], err.Error())
return 1
}
err = json.Unmarshal(subjb, &subj)
if err != nil {
fmt.Printf("ERROR: unable to read unique ID secret from %s: %s\n", args[2], err.Error())
return 1
}
csr, err := zerotier.NewCertificateCSR(&cs, subj.UniqueID, subj.UniqueIDSecret)
if err != nil {
fmt.Printf("ERROR: problem creating CSR: %s\n", err.Error())
return 1
}
if len(args) == 3 {
_, _ = os.Stdout.Write(csr)
} else {
_ = ioutil.WriteFile(args[3], csr, 0644)
}
case "sign":
case "verify":
case "show":
if len(args) != 1 {
Help()
return 1
}
case "import":
case "restore":
case "export":
case "delete":
}
return 0
} }

View file

@ -76,14 +76,14 @@ Commands:
sign <identity> <file> Sign a file with an identity's key sign <identity> <file> Sign a file with an identity's key
verify <identity> <file> <sig> Verify a signature verify <identity> <file> <sig> Verify a signature
cert <command> [args] - Certificate commands cert <command> [args] - Certificate commands
newsubject <subject> <secret> Create a new subject and secret newsid [secret] Create a new subject unique ID
newcsr <subject> <secret> Create a subject CSR newcsr <subject> <secret> [csr] Create a subject CSR
sign <csr> <identity> <certificate> Sign a CSR to create a certificate sign <csr> <identity> [certificate] Sign a CSR to create a certificate
verify <certificate> Verify a certificate verify <certificate> Verify a certificate
show List certificate for current node
import <certificate> [<trust>] Import certificate into this node import <certificate> [<trust>] Import certificate into this node
export <serial> Export a certificate from this node restore Re-import default certificates
delete <serial> Delete certificate from this node export <serial> [path] Export a certificate from this node
delete <serial|ALL> Delete certificate from this node
An <address> may be specified as a 10-digit short ZeroTier address, a An <address> may be specified as a 10-digit short ZeroTier address, a
fingerprint containing both an address and a SHA384 hash, or an identity. fingerprint containing both an address and a SHA384 hash, or an identity.

View file

@ -17,13 +17,12 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os"
"strings" "strings"
"zerotier/pkg/zerotier" "zerotier/pkg/zerotier"
) )
func Identity(args []string) { func Identity(args []string) int {
if len(args) > 0 { if len(args) > 0 {
switch args[0] { switch args[0] {
@ -32,7 +31,7 @@ func Identity(args []string) {
if len(args) > 1 { if len(args) > 1 {
if len(args) > 2 { if len(args) > 2 {
Help() Help()
os.Exit(1) return 1
} }
switch args[1] { switch args[1] {
case "c25519", "C25519", "0": case "c25519", "C25519", "0":
@ -41,37 +40,37 @@ func Identity(args []string) {
idType = zerotier.IdentityTypeP384 idType = zerotier.IdentityTypeP384
default: default:
Help() Help()
os.Exit(1) return 1
} }
} }
id, err := zerotier.NewIdentity(idType) id, err := zerotier.NewIdentity(idType)
if err != nil { if err != nil {
fmt.Printf("ERROR: internal error generating identity: %s\n", err.Error()) fmt.Printf("ERROR: internal error generating identity: %s\n", err.Error())
os.Exit(1) return 1
} }
fmt.Println(id.PrivateKeyString()) fmt.Println(id.PrivateKeyString())
os.Exit(0) return 0
case "getpublic": case "getpublic":
if len(args) == 2 { if len(args) == 2 {
fmt.Println(readIdentity(args[1]).String()) fmt.Println(readIdentity(args[1]).String())
os.Exit(0) return 0
} }
case "fingerprint": case "fingerprint":
if len(args) == 2 { if len(args) == 2 {
fmt.Println(readIdentity(args[1]).Fingerprint().String()) fmt.Println(readIdentity(args[1]).Fingerprint().String())
os.Exit(0) return 0
} }
case "validate": case "validate":
if len(args) == 2 { if len(args) == 2 {
if readIdentity(args[1]).LocallyValidate() { if readIdentity(args[1]).LocallyValidate() {
fmt.Println("OK") fmt.Println("OK")
os.Exit(0) return 0
} }
fmt.Println("FAILED") fmt.Println("FAILED")
os.Exit(1) return 1
} }
case "sign", "verify": case "sign", "verify":
@ -80,7 +79,7 @@ func Identity(args []string) {
msg, err := ioutil.ReadFile(args[2]) msg, err := ioutil.ReadFile(args[2])
if err != nil { if err != nil {
fmt.Printf("ERROR: unable to read input file: %s\n", err.Error()) fmt.Printf("ERROR: unable to read input file: %s\n", err.Error())
os.Exit(1) return 1
} }
if args[0] == "verify" { if args[0] == "verify" {
@ -88,28 +87,29 @@ func Identity(args []string) {
sig, err := hex.DecodeString(strings.TrimSpace(args[3])) sig, err := hex.DecodeString(strings.TrimSpace(args[3]))
if err != nil { if err != nil {
fmt.Println("FAILED") fmt.Println("FAILED")
os.Exit(1) return 1
} }
if id.Verify(msg, sig) { if id.Verify(msg, sig) {
fmt.Println("OK") fmt.Println("OK")
os.Exit(0) return 0
} }
} }
fmt.Println("FAILED") fmt.Println("FAILED")
os.Exit(1) return 1
} else { } else {
sig, err := id.Sign(msg) sig, err := id.Sign(msg)
if err != nil { if err != nil {
fmt.Printf("ERROR: internal error signing message: %s\n", err.Error()) fmt.Printf("ERROR: internal error signing message: %s\n", err.Error())
os.Exit(1) return 1
} }
fmt.Println(hex.EncodeToString(sig)) fmt.Println(hex.EncodeToString(sig))
os.Exit(0) return 0
} }
} }
} }
} }
Help() Help()
os.Exit(1) return 1
} }

View file

@ -23,25 +23,23 @@ import (
"zerotier/pkg/zerotier" "zerotier/pkg/zerotier"
) )
func Join(basePath, authToken string, args []string) { func Join(basePath, authToken string, args []string) int {
joinOpts := flag.NewFlagSet("join", flag.ContinueOnError) joinOpts := flag.NewFlagSet("join", flag.ContinueOnError)
controllerAuthToken := joinOpts.String("a", "", "") controllerAuthToken := joinOpts.String("a", "", "")
controllerFingerprint := joinOpts.String("c", "", "") controllerFingerprint := joinOpts.String("c", "", "")
err := joinOpts.Parse(os.Args[1:]) err := joinOpts.Parse(os.Args[1:])
if err != nil { if err != nil {
Help() Help()
os.Exit(1) return 1
return
} }
args = joinOpts.Args() args = joinOpts.Args()
if len(args) < 1 { if len(args) < 1 {
Help() Help()
os.Exit(1) return 1
return
} }
if len(args[0]) != zerotier.NetworkIDStringLength { if len(args[0]) != zerotier.NetworkIDStringLength {
fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) fmt.Printf("ERROR: invalid network ID: %s\n", args[0])
os.Exit(1) return 1
} }
_ = *controllerAuthToken // TODO: not implemented yet _ = *controllerAuthToken // TODO: not implemented yet
@ -52,13 +50,13 @@ func Join(basePath, authToken string, args []string) {
fp, err = zerotier.NewFingerprintFromString(*controllerFingerprint) fp, err = zerotier.NewFingerprintFromString(*controllerFingerprint)
if err != nil { if err != nil {
fmt.Printf("ERROR: invalid network controller fingerprint: %s\n", *controllerFingerprint) fmt.Printf("ERROR: invalid network controller fingerprint: %s\n", *controllerFingerprint)
os.Exit(1) return 1
} }
} else { } else {
id, err := zerotier.NewIdentityFromString(*controllerFingerprint) id, err := zerotier.NewIdentityFromString(*controllerFingerprint)
if err != nil { if err != nil {
fmt.Printf("ERROR: invalid network controller identity: %s\n", *controllerFingerprint) fmt.Printf("ERROR: invalid network controller identity: %s\n", *controllerFingerprint)
os.Exit(1) return 1
} }
fp = id.Fingerprint() fp = id.Fingerprint()
} }
@ -67,7 +65,7 @@ func Join(basePath, authToken string, args []string) {
nwid, err := strconv.ParseUint(args[0], 16, 64) nwid, err := strconv.ParseUint(args[0], 16, 64)
if err != nil { if err != nil {
fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) fmt.Printf("ERROR: invalid network ID: %s\n", args[0])
os.Exit(1) return 1
} }
nwids := fmt.Sprintf("%.16x", nwid) nwids := fmt.Sprintf("%.16x", nwid)
@ -85,5 +83,5 @@ func Join(basePath, authToken string, args []string) {
} }
} }
os.Exit(0) return 0
} }

View file

@ -15,29 +15,29 @@ package cli
import ( import (
"fmt" "fmt"
"os"
"strconv" "strconv"
"zerotier/pkg/zerotier" "zerotier/pkg/zerotier"
) )
func Leave(basePath, authToken string, args []string) { func Leave(basePath, authToken string, args []string) int {
if len(args) != 1 { if len(args) != 1 {
Help() Help()
os.Exit(1) return 1
} }
if len(args[0]) != zerotier.NetworkIDStringLength { if len(args[0]) != zerotier.NetworkIDStringLength {
fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) fmt.Printf("ERROR: invalid network ID: %s\n", args[0])
os.Exit(1) return 1
} }
nwid, err := strconv.ParseUint(args[0], 16, 64) nwid, err := strconv.ParseUint(args[0], 16, 64)
if err != nil { if err != nil {
fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) fmt.Printf("ERROR: invalid network ID: %s\n", args[0])
os.Exit(1) return 1
} }
nwids := fmt.Sprintf("%.16x", nwid) nwids := fmt.Sprintf("%.16x", nwid)
apiDelete(basePath, authToken, "/network/"+nwids, nil) apiDelete(basePath, authToken, "/network/"+nwids, nil)
fmt.Printf("OK %s", nwids) fmt.Printf("OK %s", nwids)
os.Exit(0)
return 0
} }

View file

@ -15,7 +15,6 @@ package cli
import ( import (
"fmt" "fmt"
"os"
"strconv" "strconv"
"strings" "strings"
@ -85,20 +84,20 @@ func showNetwork(nwids string, network *zerotier.APINetwork, jsonOutput bool) {
} }
} }
func Network(basePath, authToken string, args []string, jsonOutput bool) { func Network(basePath, authToken string, args []string, jsonOutput bool) int {
if len(args) < 1 { if len(args) < 1 {
Help() Help()
os.Exit(1) return 1
} }
if len(args[0]) != zerotier.NetworkIDStringLength { if len(args[0]) != zerotier.NetworkIDStringLength {
fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) fmt.Printf("ERROR: invalid network ID: %s\n", args[0])
os.Exit(1) return 1
} }
nwid, err := strconv.ParseUint(args[0], 16, 64) nwid, err := strconv.ParseUint(args[0], 16, 64)
if err != nil { if err != nil {
fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) fmt.Printf("ERROR: invalid network ID: %s\n", args[0])
os.Exit(1) return 1
} }
nwids := fmt.Sprintf("%.16x", nwid) nwids := fmt.Sprintf("%.16x", nwid)
@ -114,6 +113,7 @@ func Network(basePath, authToken string, args []string, jsonOutput bool) {
case "set": case "set":
if len(args) > 3 { if len(args) > 3 {
Help() Help()
return 1
} else if len(args) > 2 { } else if len(args) > 2 {
fieldName := strings.ToLower(strings.TrimSpace(args[2])) fieldName := strings.ToLower(strings.TrimSpace(args[2]))
var field *bool var field *bool
@ -130,7 +130,7 @@ func Network(basePath, authToken string, args []string, jsonOutput bool) {
field = &network.Settings.AllowDefaultRouteOverride field = &network.Settings.AllowDefaultRouteOverride
default: default:
Help() Help()
os.Exit(1) return 1
} }
if len(args) == 3 { if len(args) == 3 {
@ -148,5 +148,5 @@ func Network(basePath, authToken string, args []string, jsonOutput bool) {
} }
} }
os.Exit(0) return 0
} }

View file

@ -15,12 +15,10 @@ package cli
import ( import (
"fmt" "fmt"
"os"
"zerotier/pkg/zerotier" "zerotier/pkg/zerotier"
) )
func Networks(basePath, authToken string, args []string, jsonOutput bool) { func Networks(basePath, authToken string, args []string, jsonOutput bool) int {
var networks []zerotier.APINetwork var networks []zerotier.APINetwork
apiGet(basePath, authToken, "/network", &networks) apiGet(basePath, authToken, "/network", &networks)
@ -44,5 +42,5 @@ func Networks(basePath, authToken string, args []string, jsonOutput bool) {
} }
} }
os.Exit(0) return 0
} }

View file

@ -15,13 +15,12 @@ package cli
import ( import (
"fmt" "fmt"
"os"
"strings" "strings"
"zerotier/pkg/zerotier" "zerotier/pkg/zerotier"
) )
func Peers(basePath, authToken string, args []string, jsonOutput bool, rootsOnly bool) { func Peers(basePath, authToken string, args []string, jsonOutput bool, rootsOnly bool) int {
var peers []zerotier.Peer var peers []zerotier.Peer
apiGet(basePath, authToken, "/peer", &peers) apiGet(basePath, authToken, "/peer", &peers)
@ -64,5 +63,5 @@ func Peers(basePath, authToken string, args []string, jsonOutput bool, rootsOnly
} }
} }
os.Exit(0) return 0
} }

View file

@ -25,10 +25,10 @@ import (
"zerotier/pkg/zerotier" "zerotier/pkg/zerotier"
) )
func Service(basePath string, args []string) { func Service(basePath string, args []string) int {
if len(args) > 0 { if len(args) > 0 {
Help() Help()
os.Exit(1) return 1
} }
pidPath := path.Join(basePath, "zerotier.pid") pidPath := path.Join(basePath, "zerotier.pid")
@ -45,5 +45,5 @@ func Service(basePath string, args []string) {
} }
_ = os.Remove(pidPath) _ = os.Remove(pidPath)
os.Exit(1) return 0
} }

View file

@ -13,5 +13,6 @@
package cli package cli
func Set(basePath, authToken string, args []string) { func Set(basePath, authToken string, args []string) int {
return 0
} }

View file

@ -15,12 +15,10 @@ package cli
import ( import (
"fmt" "fmt"
"os"
"zerotier/pkg/zerotier" "zerotier/pkg/zerotier"
) )
func Status(basePath, authToken string, args []string, jsonOutput bool) { func Status(basePath, authToken string, args []string, jsonOutput bool) int {
var status zerotier.APIStatus var status zerotier.APIStatus
apiGet(basePath, authToken, "/status", &status) apiGet(basePath, authToken, "/status", &status)
@ -70,5 +68,5 @@ func Status(basePath, authToken string, args []string, jsonOutput bool) {
fmt.Printf("\n") fmt.Printf("\n")
} }
os.Exit(0) return 0
} }

View file

@ -27,33 +27,30 @@ import (
"zerotier/pkg/zerotier" "zerotier/pkg/zerotier"
) )
func getAuthTokenPaths(basePath string) (p []string) { func authToken(basePath, tflag, tTflag string) string {
p = append(p, path.Join(basePath, "authtoken.secret"))
userHome, _ := os.UserHomeDir()
if len(userHome) > 0 {
if runtime.GOOS == "darwin" {
p = append(p, path.Join(userHome, "Library", "Application Support", "ZeroTier", "authtoken.secret"))
p = append(p, path.Join(userHome, "Library", "Application Support", "ZeroTier", "One", "authtoken.secret"))
}
p = append(p, path.Join(userHome, ".zerotierauth"))
p = append(p, path.Join(userHome, ".zeroTierOneAuthToken"))
}
return p
}
func authTokenRequired(basePath, tflag, tTflag string) string {
authTokenPaths := getAuthTokenPaths(basePath)
var authToken string var authToken string
if len(tflag) > 0 { if len(tflag) > 0 {
at, err := ioutil.ReadFile(tflag) at, err := ioutil.ReadFile(tflag)
if err != nil || len(at) == 0 { if err != nil || len(at) == 0 {
fmt.Println("FATAL: unable to read local service API authorization token from " + tflag) fmt.Println("FATAL: unable to read local service API authorization token from " + tflag)
os.Exit(1) return ""
} }
authToken = string(at) authToken = string(at)
} else if len(tTflag) > 0 { } else if len(tTflag) > 0 {
authToken = tTflag authToken = tTflag
} else { } else {
var authTokenPaths []string
authTokenPaths = append(authTokenPaths, path.Join(basePath, "authtoken.secret"))
userHome, _ := os.UserHomeDir()
if len(userHome) > 0 {
if runtime.GOOS == "darwin" {
authTokenPaths = append(authTokenPaths, path.Join(userHome, "Library", "Application Support", "ZeroTier", "authtoken.secret"))
authTokenPaths = append(authTokenPaths, path.Join(userHome, "Library", "Application Support", "ZeroTier", "One", "authtoken.secret"))
}
authTokenPaths = append(authTokenPaths, path.Join(userHome, ".zerotierauth"))
authTokenPaths = append(authTokenPaths, path.Join(userHome, ".zeroTierOneAuthToken"))
}
for _, p := range authTokenPaths { for _, p := range authTokenPaths {
tmp, _ := ioutil.ReadFile(p) tmp, _ := ioutil.ReadFile(p)
if len(tmp) > 0 { if len(tmp) > 0 {
@ -61,19 +58,22 @@ func authTokenRequired(basePath, tflag, tTflag string) string {
break break
} }
} }
if len(authToken) == 0 { if len(authToken) == 0 {
fmt.Println("FATAL: unable to read local service API authorization token from any of:") fmt.Println("FATAL: unable to read local service API authorization token from any of:")
for _, p := range authTokenPaths { for _, p := range authTokenPaths {
fmt.Println(" " + p) fmt.Println(" " + p)
} }
os.Exit(1) return ""
} }
} }
authToken = strings.TrimSpace(authToken) authToken = strings.TrimSpace(authToken)
if len(authToken) == 0 { if len(authToken) == 0 {
fmt.Println("FATAL: unable to read API authorization token from command line or any filesystem location.") fmt.Println("FATAL: unable to read API authorization token from command line or any filesystem location.")
os.Exit(1) return ""
} }
return authToken return authToken
} }
@ -111,37 +111,39 @@ func main() {
basePath = *pflag basePath = *pflag
} }
exitCode := 0
switch args[0] { switch args[0] {
default: default:
//case "help": cli.Help()
exitCode = 1
case "help":
cli.Help() cli.Help()
case "version": case "version":
fmt.Printf("%d.%d.%d\n", zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision) fmt.Printf("%d.%d.%d\n", zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision)
case "service": case "service":
cli.Service(basePath, cmdArgs) exitCode = cli.Service(basePath, cmdArgs)
case "status", "info": case "status", "info":
cli.Status(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs, *jflag) exitCode = cli.Status(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag)
case "join": case "join":
cli.Join(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs) exitCode = cli.Join(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs)
case "leave": case "leave":
cli.Leave(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs) exitCode = cli.Leave(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs)
case "networks", "listnetworks": case "networks", "listnetworks":
cli.Networks(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs, *jflag) exitCode = cli.Networks(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag)
case "network": case "network":
cli.Network(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs, *jflag) exitCode = cli.Network(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag)
case "peers", "listpeers", "lspeers": case "peers", "listpeers", "lspeers":
cli.Peers(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs, *jflag, false) exitCode = cli.Peers(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag, false)
case "peer": case "peer":
authTokenRequired(basePath, *tflag, *tTflag)
case "roots": case "roots":
cli.Peers(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs, *jflag, true) exitCode = cli.Peers(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag, true)
case "controller": case "controller":
case "set": case "set":
cli.Set(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs) exitCode = cli.Set(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs)
case "identity": case "identity":
cli.Identity(cmdArgs) exitCode = cli.Identity(cmdArgs)
case "cert": case "cert":
cli.Cert(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs, *jflag) exitCode = cli.Cert(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag)
} }
os.Exit(0) os.Exit(exitCode)
} }

View file

@ -106,7 +106,9 @@
* 9 - 1.2.0 ... 1.2.14 * 9 - 1.2.0 ... 1.2.14
* 10 - 1.4.0 ... 1.4.6 * 10 - 1.4.0 ... 1.4.6
* + Contained early pre-alpha versions of multipath, which are deprecated * + Contained early pre-alpha versions of multipath, which are deprecated
* 11 - 2.0.0 ... CURRENT * 11 - 1.4.8 ... CURRENT 1.4
* + AES-GMAC-SIV backported for faster peer-to-peer crypto
* 20 - 2.0.0 ... CURRENT
* + New more WAN-efficient P2P-assisted multicast algorithm * + New more WAN-efficient P2P-assisted multicast algorithm
* + HELLO and OK(HELLO) include an extra HMAC to harden authentication * + HELLO and OK(HELLO) include an extra HMAC to harden authentication
* + HELLO and OK(HELLO) carry meta-data in a dictionary that's encrypted * + HELLO and OK(HELLO) carry meta-data in a dictionary that's encrypted
@ -117,7 +119,7 @@
* + Short probe packets to reduce probe bandwidth * + Short probe packets to reduce probe bandwidth
* + More aggressive NAT traversal techniques for IPv4 symmetric NATs * + More aggressive NAT traversal techniques for IPv4 symmetric NATs
*/ */
#define ZT_PROTO_VERSION 11 #define ZT_PROTO_VERSION 20
/** /**
* Minimum supported protocol version * Minimum supported protocol version

View file

@ -16,11 +16,13 @@
#include "Constants.hpp" #include "Constants.hpp"
#include <stddef.h>
#include <stdarg.h>
#include <utility> #include <utility>
#include <algorithm> #include <algorithm>
#include <memory> #include <memory>
#include <stdint.h> #include <stdexcept>
#include <stddef.h>
namespace ZeroTier { namespace ZeroTier {

View file

@ -87,6 +87,12 @@ type Certificate struct {
Signature []byte `json:"signature,omitempty"` Signature []byte `json:"signature,omitempty"`
} }
// CertificateSubjectUniqueIDSecret bundles a certificate subject unique ID and its secret key.
type CertificateSubjectUniqueIDSecret struct {
UniqueID []byte `json:"uniqueId,omitempty"`
UniqueIDSecret []byte `json:"uniqueIdSecret,omitempty"`
}
func certificateErrorToError(cerr int) error { func certificateErrorToError(cerr int) error {
switch cerr { switch cerr {
case C.ZT_CERTIFICATE_ERROR_NONE: case C.ZT_CERTIFICATE_ERROR_NONE: