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"
)
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()
set(GO_BUILD_TAGS)

View file

@ -13,5 +13,101 @@
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
verify <identity> <file> <sig> Verify a signature
cert <command> [args] - Certificate commands
newsubject <subject> <secret> Create a new subject and secret
newcsr <subject> <secret> Create a subject CSR
sign <csr> <identity> <certificate> Sign a CSR to create a certificate
newsid [secret] Create a new subject unique ID
newcsr <subject> <secret> [csr] Create a subject CSR
sign <csr> <identity> [certificate] Sign a CSR to create a certificate
verify <certificate> Verify a certificate
show List certificate for current node
import <certificate> [<trust>] Import certificate into this node
export <serial> Export a certificate from this node
delete <serial> Delete certificate from this node
restore Re-import default certificates
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
fingerprint containing both an address and a SHA384 hash, or an identity.

View file

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

View file

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

View file

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

View file

@ -13,5 +13,6 @@
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 (
"fmt"
"os"
"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
apiGet(basePath, authToken, "/status", &status)
@ -70,5 +68,5 @@ func Status(basePath, authToken string, args []string, jsonOutput bool) {
fmt.Printf("\n")
}
os.Exit(0)
return 0
}

View file

@ -27,33 +27,30 @@ import (
"zerotier/pkg/zerotier"
)
func getAuthTokenPaths(basePath string) (p []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)
func authToken(basePath, tflag, tTflag string) string {
var authToken string
if len(tflag) > 0 {
at, err := ioutil.ReadFile(tflag)
if err != nil || len(at) == 0 {
fmt.Println("FATAL: unable to read local service API authorization token from " + tflag)
os.Exit(1)
return ""
}
authToken = string(at)
} else if len(tTflag) > 0 {
authToken = tTflag
} 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 {
tmp, _ := ioutil.ReadFile(p)
if len(tmp) > 0 {
@ -61,19 +58,22 @@ func authTokenRequired(basePath, tflag, tTflag string) string {
break
}
}
if len(authToken) == 0 {
fmt.Println("FATAL: unable to read local service API authorization token from any of:")
for _, p := range authTokenPaths {
fmt.Println(" " + p)
}
os.Exit(1)
return ""
}
}
authToken = strings.TrimSpace(authToken)
if len(authToken) == 0 {
fmt.Println("FATAL: unable to read API authorization token from command line or any filesystem location.")
os.Exit(1)
return ""
}
return authToken
}
@ -111,37 +111,39 @@ func main() {
basePath = *pflag
}
exitCode := 0
switch args[0] {
default:
//case "help":
cli.Help()
exitCode = 1
case "help":
cli.Help()
case "version":
fmt.Printf("%d.%d.%d\n", zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision)
case "service":
cli.Service(basePath, cmdArgs)
exitCode = cli.Service(basePath, cmdArgs)
case "status", "info":
cli.Status(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs, *jflag)
exitCode = cli.Status(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag)
case "join":
cli.Join(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs)
exitCode = cli.Join(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs)
case "leave":
cli.Leave(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs)
exitCode = cli.Leave(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs)
case "networks", "listnetworks":
cli.Networks(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs, *jflag)
exitCode = cli.Networks(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag)
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":
cli.Peers(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs, *jflag, false)
exitCode = cli.Peers(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag, false)
case "peer":
authTokenRequired(basePath, *tflag, *tTflag)
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 "set":
cli.Set(basePath, authTokenRequired(basePath, *tflag, *tTflag), cmdArgs)
exitCode = cli.Set(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs)
case "identity":
cli.Identity(cmdArgs)
exitCode = cli.Identity(cmdArgs)
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
* 10 - 1.4.0 ... 1.4.6
* + 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
* + HELLO and OK(HELLO) include an extra HMAC to harden authentication
* + HELLO and OK(HELLO) carry meta-data in a dictionary that's encrypted
@ -117,7 +119,7 @@
* + Short probe packets to reduce probe bandwidth
* + More aggressive NAT traversal techniques for IPv4 symmetric NATs
*/
#define ZT_PROTO_VERSION 11
#define ZT_PROTO_VERSION 20
/**
* Minimum supported protocol version

View file

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

View file

@ -87,6 +87,12 @@ type Certificate struct {
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 {
switch cerr {
case C.ZT_CERTIFICATE_ERROR_NONE: