mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2024-12-18 20:47:53 +00:00
Identity management plumbing to Go
This commit is contained in:
parent
7fc78129f4
commit
47a08ccbd4
@ -48,7 +48,7 @@ Commands:
|
||||
newdnskey Create a secure DNS name and secret
|
||||
getdns <key> <locator> Create secure DNS TXT records
|
||||
identity <command> [args] Identity management commands
|
||||
new Create new identity (including secret)
|
||||
new [c25519|p384] Create new identity (including secret)
|
||||
getpublic <identity> Extract only public part of identity
|
||||
validate <identity> Locally validate an identity
|
||||
sign <identity> <file> Sign a file with an identity's key
|
||||
|
@ -13,6 +13,135 @@
|
||||
|
||||
package cli
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"zerotier/pkg/zerotier"
|
||||
)
|
||||
|
||||
/*
|
||||
identity <command> [args] Identity management commands
|
||||
new Create new identity (including secret)
|
||||
getpublic <identity> Extract only public part of identity
|
||||
validate <identity> Locally validate an identity
|
||||
sign <identity> <file> Sign a file with an identity's key
|
||||
verify <identity> <file> <sig> Verify a signature
|
||||
*/
|
||||
|
||||
// Identity command
|
||||
func Identity(args []string) {
|
||||
if len(args) > 0 {
|
||||
switch args[0] {
|
||||
|
||||
case "new":
|
||||
idType := zerotier.IdentityTypeC25519
|
||||
if len(args) > 1 {
|
||||
if len(args) > 2 {
|
||||
Help()
|
||||
os.Exit(1)
|
||||
}
|
||||
switch args[1] {
|
||||
case "c25519":
|
||||
case "p384":
|
||||
idType = zerotier.IdentityTypeP384
|
||||
default:
|
||||
Help()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
id, err := zerotier.NewIdentity(idType)
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: internal error generating identity: %s\n", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(id.PrivateKeyString())
|
||||
os.Exit(0)
|
||||
|
||||
case "getpublic":
|
||||
if len(args) == 2 {
|
||||
idData, err := ioutil.ReadFile(args[1])
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: unable to read identity: %s\n", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
id, err := zerotier.NewIdentityFromString(string(idData))
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: identity in file '%s' invalid: %s\n", args[1], err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(id.String())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
case "validate":
|
||||
if len(args) == 2 {
|
||||
idData, err := ioutil.ReadFile(args[1])
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: unable to read identity: %s\n", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
id, err := zerotier.NewIdentityFromString(string(idData))
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: identity in file '%s' invalid: %s\n", args[1], err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
if id.LocallyValidate() {
|
||||
fmt.Println("OK")
|
||||
os.Exit(0)
|
||||
}
|
||||
fmt.Println("FAILED")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
case "sign", "verify":
|
||||
if len(args) > 2 {
|
||||
idData, err := ioutil.ReadFile(args[1])
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: unable to read identity: %s\n", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
id, err := zerotier.NewIdentityFromString(string(idData))
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: identity in file '%s' invalid: %s\n", args[1], err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
msg, err := ioutil.ReadFile(args[2])
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: unable to read input file: %s\n", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if args[0] == "verify" {
|
||||
if len(args) == 4 {
|
||||
sig, err := hex.DecodeString(strings.TrimSpace(args[3]))
|
||||
if err != nil {
|
||||
fmt.Println("FAILED")
|
||||
os.Exit(1)
|
||||
}
|
||||
if id.Verify(msg, sig) {
|
||||
fmt.Println("OK")
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
fmt.Println("FAILED")
|
||||
os.Exit(1)
|
||||
} else {
|
||||
sig, err := id.Sign(msg)
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR: internal error signing message: %s\n", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(hex.EncodeToString(sig))
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Help()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
@ -116,6 +116,16 @@ func locatorGetDNS(args []string) {
|
||||
fmt.Printf("FATAL: locator invalid: %s", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
txt, err := loc.MakeTXTRecords(&sk)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: error creating TXT records: %s\n", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, t := range txt {
|
||||
fmt.Println(t)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Locator CLI command
|
||||
|
@ -723,6 +723,44 @@ extern "C" int ZT_GoTap_removeRoute(ZT_GoTap *tap,int targetAf,const void *targe
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
extern "C" const char *ZT_GoIdentity_generate(int type)
|
||||
{
|
||||
Identity id;
|
||||
id.generate((Identity::Type)type);
|
||||
char *tmp = (char *)malloc(ZT_IDENTITY_STRING_BUFFER_LENGTH);
|
||||
if (tmp)
|
||||
id.toString(true,tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
extern "C" int ZT_GoIdentity_validate(const char *idStr)
|
||||
{
|
||||
Identity id;
|
||||
if (!id.fromString(idStr))
|
||||
return 0;
|
||||
if (!id.locallyValidate())
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern "C" int ZT_GoIdentity_sign(const char *idStr,const void *data,unsigned int len,void *sigbuf,unsigned int sigbuflen)
|
||||
{
|
||||
Identity id;
|
||||
if (!id.fromString(idStr))
|
||||
return 0;
|
||||
return (int)id.sign(data,len,sigbuf,sigbuflen);
|
||||
}
|
||||
|
||||
extern "C" int ZT_GoIdentity_verify(const char *idStr,const void *data,unsigned int len,const void *sig,unsigned int siglen)
|
||||
{
|
||||
Identity id;
|
||||
if (!id.fromString(idStr))
|
||||
return 0;
|
||||
return id.verify(data,len,sig,siglen) ? 1 : 0;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
extern "C" int ZT_GoLocator_makeSecureDNSName(char *name,unsigned int nameBufSize,uint8_t *privateKey,unsigned int privateKeyBufSize)
|
||||
{
|
||||
if ((privateKeyBufSize < ZT_ECC384_PRIVATE_KEY_SIZE)||(nameBufSize < 256))
|
||||
|
@ -90,6 +90,16 @@ int ZT_GoTap_removeRoute(ZT_GoTap *tap,int targetAf,const void *targetIp,int tar
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
const char *ZT_GoIdentity_generate(int type);
|
||||
|
||||
int ZT_GoIdentity_validate(const char *idStr);
|
||||
|
||||
int ZT_GoIdentity_sign(const char *idStr,const void *data,unsigned int len,void *sigbuf,unsigned int sigbuflen);
|
||||
|
||||
int ZT_GoIdentity_verify(const char *idStr,const void *data,unsigned int len,const void *sig,unsigned int siglen);
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
struct ZT_GoLocator_Info {
|
||||
char id[1024];
|
||||
unsigned int phyCount;
|
||||
|
@ -13,11 +13,16 @@
|
||||
|
||||
package zerotier
|
||||
|
||||
//#cgo CFLAGS: -O3
|
||||
//#include "../../native/GoGlue.h"
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// IdentityTypeC25519 is a classic Curve25519/Ed25519 identity
|
||||
@ -42,6 +47,17 @@ type Identity struct {
|
||||
privateKey []byte
|
||||
}
|
||||
|
||||
// NewIdentity generates a new identity of the selected type
|
||||
func NewIdentity(identityType int) (*Identity, error) {
|
||||
cIdStr := C.ZT_GoIdentity_generate(C.int(identityType))
|
||||
if uintptr(unsafe.Pointer(cIdStr)) == 0 {
|
||||
return nil, ErrInternal
|
||||
}
|
||||
id, err := NewIdentityFromString(C.GoString(cIdStr))
|
||||
C.free(unsafe.Pointer(cIdStr))
|
||||
return id, err
|
||||
}
|
||||
|
||||
// NewIdentityFromString generates a new identity from its string representation.
|
||||
// The private key is imported as well if it is present.
|
||||
func NewIdentityFromString(s string) (*Identity, error) {
|
||||
@ -80,7 +96,7 @@ func NewIdentityFromString(s string) (*Identity, error) {
|
||||
}
|
||||
|
||||
case 1:
|
||||
id.publicKey, err = base32StdLowerCase.DecodeString(ss[2])
|
||||
id.publicKey, err = Base32StdLowerCase.DecodeString(ss[2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -88,7 +104,7 @@ func NewIdentityFromString(s string) (*Identity, error) {
|
||||
return nil, ErrInvalidKey
|
||||
}
|
||||
if len(ss) >= 4 {
|
||||
id.privateKey, err = base32StdLowerCase.DecodeString(ss[3])
|
||||
id.privateKey, err = Base32StdLowerCase.DecodeString(ss[3])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -114,7 +130,7 @@ func (id *Identity) PrivateKeyString() string {
|
||||
}
|
||||
case IdentityTypeP384:
|
||||
if len(id.publicKey) == IdentityTypeP384PublicKeySize && len(id.privateKey) == IdentityTypeP384PrivateKeySize {
|
||||
return fmt.Sprintf("%.10x:1:%s:%s", uint64(id.address), base32StdLowerCase.EncodeToString(id.publicKey), base32StdLowerCase.EncodeToString(id.privateKey))
|
||||
return fmt.Sprintf("%.10x:1:%s:%s", uint64(id.address), Base32StdLowerCase.EncodeToString(id.publicKey), Base32StdLowerCase.EncodeToString(id.privateKey))
|
||||
}
|
||||
}
|
||||
return ""
|
||||
@ -130,12 +146,49 @@ func (id *Identity) String() string {
|
||||
}
|
||||
case IdentityTypeP384:
|
||||
if len(id.publicKey) == IdentityTypeP384PublicKeySize {
|
||||
return fmt.Sprintf("%.10x:1:%s", uint64(id.address), base32StdLowerCase.EncodeToString(id.publicKey))
|
||||
return fmt.Sprintf("%.10x:1:%s", uint64(id.address), Base32StdLowerCase.EncodeToString(id.publicKey))
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// LocallyValidate performs local self-validation of this identity
|
||||
func (id *Identity) LocallyValidate() bool {
|
||||
idCStr := C.CString(id.String())
|
||||
defer C.free(unsafe.Pointer(idCStr))
|
||||
return C.ZT_GoIdentity_validate(idCStr) != 0
|
||||
}
|
||||
|
||||
// Sign signs a message with this identity
|
||||
func (id *Identity) Sign(msg []byte) ([]byte, error) {
|
||||
idCStr := C.CString(id.PrivateKeyString())
|
||||
var sigbuf [96]byte
|
||||
var dataP unsafe.Pointer
|
||||
if len(msg) > 0 {
|
||||
dataP = unsafe.Pointer(&msg[0])
|
||||
}
|
||||
siglen := C.ZT_GoIdentity_sign(idCStr, dataP, C.uint(len(msg)), unsafe.Pointer(&sigbuf[0]), C.uint(len(sigbuf)))
|
||||
C.free(unsafe.Pointer(idCStr))
|
||||
if siglen <= 0 {
|
||||
return nil, ErrInvalidKey
|
||||
}
|
||||
return sigbuf[0:int(siglen)], nil
|
||||
}
|
||||
|
||||
// Verify verifies a signature
|
||||
func (id *Identity) Verify(msg, sig []byte) bool {
|
||||
if len(sig) == 0 {
|
||||
return false
|
||||
}
|
||||
idCStr := C.CString(id.String())
|
||||
defer C.free(unsafe.Pointer(idCStr))
|
||||
var dataP unsafe.Pointer
|
||||
if len(msg) > 0 {
|
||||
dataP = unsafe.Pointer(&msg[0])
|
||||
}
|
||||
return C.ZT_GoIdentity_verify(idCStr, 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)
|
||||
func (id *Identity) MarshalJSON() ([]byte, error) {
|
||||
return []byte("\"" + id.String() + "\""), nil
|
||||
|
@ -24,7 +24,8 @@ import (
|
||||
// ZeroTierLogoChar is the unicode character that is ZeroTier's logo
|
||||
const ZeroTierLogoChar = "⏁"
|
||||
|
||||
var base32StdLowerCase = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567").WithPadding(base32.NoPadding)
|
||||
// Base32StdLowerCase is a base32 encoder/decoder using a lower-case standard alphabet and no padding.
|
||||
var Base32StdLowerCase = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567").WithPadding(base32.NoPadding)
|
||||
|
||||
// TimeMs returns the time in milliseconds since epoch.
|
||||
func TimeMs() int64 { return int64(time.Now().UnixNano()) / int64(1000000) }
|
||||
|
Loading…
Reference in New Issue
Block a user