mirror of
https://github.com/ZeroTier/ZeroTierOne
synced 2025-08-21 05:43:59 -07:00
More certificate plumbing.
This commit is contained in:
parent
e5e6f82a8e
commit
7e341ed397
4 changed files with 200 additions and 2 deletions
|
@ -629,6 +629,8 @@ int ZT_Certificate_sign(
|
||||||
void *signedCert,
|
void *signedCert,
|
||||||
int *signedCertSize)
|
int *signedCertSize)
|
||||||
{
|
{
|
||||||
|
if (!cert)
|
||||||
|
return ZT_RESULT_ERROR_BAD_PARAMETER;
|
||||||
ZeroTier::Certificate c(*cert);
|
ZeroTier::Certificate c(*cert);
|
||||||
if (!c.sign(*reinterpret_cast<const ZeroTier::Identity *>(signer)))
|
if (!c.sign(*reinterpret_cast<const ZeroTier::Identity *>(signer)))
|
||||||
return ZT_RESULT_ERROR_BAD_PARAMETER;
|
return ZT_RESULT_ERROR_BAD_PARAMETER;
|
||||||
|
@ -669,7 +671,34 @@ enum ZT_CertificateError ZT_Certificate_decode(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ZT_SDK_API void ZT_Certificate_delete(ZT_Certificate *cert)
|
int ZT_Certificate_encode(
|
||||||
|
const ZT_Certificate *cert,
|
||||||
|
void *encoded,
|
||||||
|
int *encodedSize)
|
||||||
|
{
|
||||||
|
if ((!cert) || (!encoded) || (!encodedSize))
|
||||||
|
return ZT_RESULT_ERROR_BAD_PARAMETER;
|
||||||
|
ZeroTier::Certificate c(*cert);
|
||||||
|
ZeroTier::Vector< uint8_t > enc(c.encode());
|
||||||
|
if ((int)enc.size() > *encodedSize)
|
||||||
|
return ZT_RESULT_ERROR_BAD_PARAMETER;
|
||||||
|
ZeroTier::Utils::copy(encoded, enc.data(), (unsigned int)enc.size());
|
||||||
|
*encodedSize = (int)enc.size();
|
||||||
|
return ZT_RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ZT_CertificateError ZT_Certificate_verify(const ZT_Certificate *cert)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (!cert)
|
||||||
|
return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
|
||||||
|
return ZeroTier::Certificate(*cert).verify();
|
||||||
|
} catch ( ... ) {
|
||||||
|
return ZT_CERTIFICATE_ERROR_INVALID_FORMAT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZT_Certificate_delete(ZT_Certificate *cert)
|
||||||
{
|
{
|
||||||
if (cert)
|
if (cert)
|
||||||
delete reinterpret_cast<ZeroTier::Certificate *>(cert);
|
delete reinterpret_cast<ZeroTier::Certificate *>(cert);
|
||||||
|
|
|
@ -2764,7 +2764,7 @@ ZT_SDK_API int ZT_Certificate_newCSR(
|
||||||
int *csrSize);
|
int *csrSize);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sign a CSR to generate a complete certificate
|
* Sign a CSR to generate a complete certificate.
|
||||||
*
|
*
|
||||||
* @param cert Certificate to sign
|
* @param cert Certificate to sign
|
||||||
* @param signer Signer identity (must contain secret key)
|
* @param signer Signer identity (must contain secret key)
|
||||||
|
@ -2799,6 +2799,27 @@ ZT_SDK_API enum ZT_CertificateError ZT_Certificate_decode(
|
||||||
int certSize,
|
int certSize,
|
||||||
int verify);
|
int verify);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a certificate
|
||||||
|
*
|
||||||
|
* @param cert Certificate to encode
|
||||||
|
* @param encoded Buffer to store certificate (suggested size: 16384)
|
||||||
|
* @param encodedSize Value/result: size of certificate encoding buffer
|
||||||
|
* @return OK (0) or error
|
||||||
|
*/
|
||||||
|
ZT_SDK_API int ZT_Certificate_encode(
|
||||||
|
const ZT_Certificate *cert,
|
||||||
|
void *encoded,
|
||||||
|
int *encodedSize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify certificate signatures and internal structure.
|
||||||
|
*
|
||||||
|
* @param cert Certificate to verify
|
||||||
|
* @return Certificate error or ZT_CERTIFICATE_ERROR_NONE if no errors found.
|
||||||
|
*/
|
||||||
|
ZT_SDK_API enum ZT_CertificateError ZT_Certificate_verify(const ZT_Certificate *cert);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free a certificate created with ZT_Certificate_decode()
|
* Free a certificate created with ZT_Certificate_decode()
|
||||||
*
|
*
|
||||||
|
|
|
@ -17,12 +17,15 @@ package zerotier
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CertificateSerialNoSize = 48
|
CertificateSerialNoSize = 48
|
||||||
CertificateMaxStringLength = int(C.ZT_CERTIFICATE_MAX_STRING_LENGTH)
|
CertificateMaxStringLength = int(C.ZT_CERTIFICATE_MAX_STRING_LENGTH)
|
||||||
|
|
||||||
|
CertificateUniqueIdTypeNistP384 = int(C.ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384)
|
||||||
)
|
)
|
||||||
|
|
||||||
// CertificateName identifies a real-world entity that owns a subject or has signed a certificate.
|
// CertificateName identifies a real-world entity that owns a subject or has signed a certificate.
|
||||||
|
@ -79,6 +82,8 @@ type Certificate struct {
|
||||||
Signature []byte `json:"signature,omitempty"`
|
Signature []byte `json:"signature,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CCertificate wraps a pointer to a C ZT_Certificate with any related allocated memory.
|
||||||
|
// Only the 'C' field should be used directly, and only this field is exported.
|
||||||
type CCertificate struct {
|
type CCertificate struct {
|
||||||
C unsafe.Pointer
|
C unsafe.Pointer
|
||||||
internalCertificate C.ZT_Certificate
|
internalCertificate C.ZT_Certificate
|
||||||
|
@ -89,6 +94,65 @@ type CCertificate struct {
|
||||||
internalSubjectUpdateURLsData [][]byte
|
internalSubjectUpdateURLsData [][]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func certificateErrorToError(cerr int) error {
|
||||||
|
switch cerr {
|
||||||
|
case C.ZT_CERTIFICATE_ERROR_NONE:
|
||||||
|
return nil
|
||||||
|
case C.ZT_CERTIFICATE_ERROR_HAVE_NEWER_CERT:
|
||||||
|
return ErrCertificateHaveNewerCert
|
||||||
|
case C.ZT_CERTIFICATE_ERROR_INVALID_FORMAT:
|
||||||
|
return ErrCertificateInvalidFormat
|
||||||
|
case C.ZT_CERTIFICATE_ERROR_INVALID_IDENTITY:
|
||||||
|
return ErrCertificateInvalidIdentity
|
||||||
|
case C.ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE:
|
||||||
|
return ErrCertificateInvalidPrimarySignature
|
||||||
|
case C.ZT_CERTIFICATE_ERROR_INVALID_CHAIN:
|
||||||
|
return ErrCertificateInvalidChain
|
||||||
|
case C.ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE:
|
||||||
|
return ErrCertificateInvalidComponentSignature
|
||||||
|
case C.ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF:
|
||||||
|
return ErrCertificateInvalidUniqueIDProof
|
||||||
|
case C.ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS:
|
||||||
|
return ErrCertificateMissingRequiredFields
|
||||||
|
case C.ZT_CERTIFICATE_ERROR_OUT_OF_VALID_TIME_WINDOW:
|
||||||
|
return ErrCertificateOutOfValidTimeWindow
|
||||||
|
}
|
||||||
|
return ErrInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCertificateFromBytes decodes a certificate from an encoded byte string.
|
||||||
|
// Note that this is also used to decode a CSR. When used for a CSR only the
|
||||||
|
// Subject part of the certificate will contain anything and the rest will be
|
||||||
|
// blank. If 'verify' is true the certificate will also be verified. If using
|
||||||
|
// to decode a CSR this should be false as a CSR will not contain a full set
|
||||||
|
// of fields or a certificate signature.
|
||||||
|
func NewCertificateFromBytes(cert []byte, verify bool) (*Certificate, error) {
|
||||||
|
if len(cert) == 0 {
|
||||||
|
return nil, ErrInvalidParameter
|
||||||
|
}
|
||||||
|
var dec unsafe.Pointer
|
||||||
|
ver := C.int(0)
|
||||||
|
if verify {
|
||||||
|
ver = 1
|
||||||
|
}
|
||||||
|
cerr := C.ZT_Certificate_decode((**C.ZT_Certificate)(unsafe.Pointer(&dec)), unsafe.Pointer(&cert[0]), C.int(len(cert)), ver)
|
||||||
|
if dec != unsafe.Pointer(nil) {
|
||||||
|
defer C.ZT_Certificate_delete((*C.ZT_Certificate)(dec))
|
||||||
|
}
|
||||||
|
if cerr != 0 {
|
||||||
|
return nil, certificateErrorToError(int(cerr))
|
||||||
|
}
|
||||||
|
if dec == unsafe.Pointer(nil) {
|
||||||
|
return nil, ErrInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
goCert := NewCertificateFromCCertificate(dec)
|
||||||
|
if goCert == nil {
|
||||||
|
return nil, ErrInternal
|
||||||
|
}
|
||||||
|
return goCert, nil
|
||||||
|
}
|
||||||
|
|
||||||
// NewCertificateFromCCertificate translates a C ZT_Certificate into a Go Certificate.
|
// NewCertificateFromCCertificate translates a C ZT_Certificate into a Go Certificate.
|
||||||
func NewCertificateFromCCertificate(ccptr unsafe.Pointer) *Certificate {
|
func NewCertificateFromCCertificate(ccptr unsafe.Pointer) *Certificate {
|
||||||
cc := (*C.ZT_Certificate)(ccptr)
|
cc := (*C.ZT_Certificate)(ccptr)
|
||||||
|
@ -349,3 +413,77 @@ func (c *Certificate) CCertificate() *CCertificate {
|
||||||
|
|
||||||
return &cc
|
return &cc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Marshal encodes this certificae as a byte array.
|
||||||
|
func (c *Certificate) Marshal() ([]byte, error) {
|
||||||
|
cc := c.CCertificate()
|
||||||
|
if cc == nil {
|
||||||
|
return nil, ErrInternal
|
||||||
|
}
|
||||||
|
var encoded [16384]byte
|
||||||
|
encodedSize := C.int(16384)
|
||||||
|
rv := int(C.ZT_Certificate_encode((*C.ZT_Certificate)(cc.C), unsafe.Pointer(&encoded[0]), &encodedSize))
|
||||||
|
if rv != 0 {
|
||||||
|
return nil, fmt.Errorf("Certificate encode error %d", rv)
|
||||||
|
}
|
||||||
|
return append(make([]byte, 0, int(encodedSize)), encoded[0:int(encodedSize)]...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify returns nil on success or a certificate error if there is a problem with this certificate.
|
||||||
|
func (c *Certificate) Verify() error {
|
||||||
|
cc := c.CCertificate()
|
||||||
|
if cc == nil {
|
||||||
|
return ErrInternal
|
||||||
|
}
|
||||||
|
return certificateErrorToError(int(C.ZT_Certificate_verify((*C.ZT_Certificate)(cc.C))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCertificateSubjectUniqueId creates a new certificate subject unique ID and corresponding private key.
|
||||||
|
// Right now only one type is supported: CertificateUniqueIdTypeNistP384
|
||||||
|
func NewCertificateSubjectUniqueId(uniqueIdType int) (id []byte, priv []byte, err error) {
|
||||||
|
if uniqueIdType != CertificateUniqueIdTypeNistP384 {
|
||||||
|
err = ErrInvalidParameter
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id = make([]byte, int(C.ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE))
|
||||||
|
priv = make([]byte, int(C.ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE))
|
||||||
|
idSize := C.int(len(id))
|
||||||
|
idPrivateSize := C.int(len(priv))
|
||||||
|
if C.ZT_Certificate_newSubjectUniqueId((C.enum_ZT_CertificateUniqueIdType)(uniqueIdType), unsafe.Pointer(&id[0]), &idSize, unsafe.Pointer(&priv[0]), &idPrivateSize) != 0 {
|
||||||
|
id = nil
|
||||||
|
priv = nil
|
||||||
|
err = ErrInvalidParameter
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if int(idSize) != len(id) || int(idPrivateSize) != len(priv) {
|
||||||
|
id = nil
|
||||||
|
priv = nil
|
||||||
|
err = ErrInvalidParameter
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCertificateCSR creates a new certificate signing request (CSR) from a certificate subject and optional unique ID.
|
||||||
|
func NewCertificateCSR(subject *CertificateSubject, uniqueId []byte, uniqueIdPrivate []byte) ([]byte, error) {
|
||||||
|
var tmp Certificate
|
||||||
|
tmp.Subject = *subject
|
||||||
|
ctmp := tmp.CCertificate()
|
||||||
|
if ctmp == nil {
|
||||||
|
return nil, ErrInternal
|
||||||
|
}
|
||||||
|
ccert := (*C.ZT_Certificate)(ctmp.C)
|
||||||
|
var uid unsafe.Pointer
|
||||||
|
var uidp unsafe.Pointer
|
||||||
|
if len(uniqueId) > 0 && len(uniqueIdPrivate) > 0 {
|
||||||
|
uid = unsafe.Pointer(&uniqueId[0])
|
||||||
|
uidp = unsafe.Pointer(&uniqueIdPrivate[0])
|
||||||
|
}
|
||||||
|
var csr [16384]byte
|
||||||
|
csrSize := C.int(16384)
|
||||||
|
rv := int(C.ZT_Certificate_newCSR(&(ccert.subject), uid, C.int(len(uniqueId)), uidp, C.int(len(uniqueIdPrivate)), unsafe.Pointer(&csr[0]), &csrSize))
|
||||||
|
if rv != 0 {
|
||||||
|
return nil, fmt.Errorf("newCSR error %d", rv)
|
||||||
|
}
|
||||||
|
return append(make([]byte, 0, int(csrSize)), csr[0:int(csrSize)]...), nil
|
||||||
|
}
|
||||||
|
|
|
@ -29,6 +29,16 @@ const (
|
||||||
ErrTapInitFailed Err = "unable to create native Tap instance"
|
ErrTapInitFailed Err = "unable to create native Tap instance"
|
||||||
ErrUnrecognizedIdentityType Err = "unrecognized identity type"
|
ErrUnrecognizedIdentityType Err = "unrecognized identity type"
|
||||||
ErrInvalidKey Err = "invalid key data"
|
ErrInvalidKey Err = "invalid key data"
|
||||||
|
|
||||||
|
ErrCertificateHaveNewerCert Err = "a newer certificate for this subject unique ID is already loaded"
|
||||||
|
ErrCertificateInvalidFormat Err = "invalid certificate format"
|
||||||
|
ErrCertificateInvalidIdentity Err = "invalid identity in certificate"
|
||||||
|
ErrCertificateInvalidPrimarySignature Err = "invalid primary signature"
|
||||||
|
ErrCertificateInvalidChain Err = "certificate chain verification failed"
|
||||||
|
ErrCertificateInvalidComponentSignature Err = "an internal component of this certificate has an invalid signature"
|
||||||
|
ErrCertificateInvalidUniqueIDProof Err = "certificate subject unique ID proof signature verification failed"
|
||||||
|
ErrCertificateMissingRequiredFields Err = "certificate is missing one or more required fields"
|
||||||
|
ErrCertificateOutOfValidTimeWindow Err = "certificate is out of its valid time window"
|
||||||
)
|
)
|
||||||
|
|
||||||
// APIErr is returned by the JSON API when a call fails
|
// APIErr is returned by the JSON API when a call fails
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue