mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-07-21 01:42:50 +02: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,
|
||||
int *signedCertSize)
|
||||
{
|
||||
if (!cert)
|
||||
return ZT_RESULT_ERROR_BAD_PARAMETER;
|
||||
ZeroTier::Certificate c(*cert);
|
||||
if (!c.sign(*reinterpret_cast<const ZeroTier::Identity *>(signer)))
|
||||
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)
|
||||
delete reinterpret_cast<ZeroTier::Certificate *>(cert);
|
||||
|
|
|
@ -2764,7 +2764,7 @@ ZT_SDK_API int ZT_Certificate_newCSR(
|
|||
int *csrSize);
|
||||
|
||||
/**
|
||||
* Sign a CSR to generate a complete certificate
|
||||
* Sign a CSR to generate a complete certificate.
|
||||
*
|
||||
* @param cert Certificate to sign
|
||||
* @param signer Signer identity (must contain secret key)
|
||||
|
@ -2799,6 +2799,27 @@ ZT_SDK_API enum ZT_CertificateError ZT_Certificate_decode(
|
|||
int certSize,
|
||||
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()
|
||||
*
|
||||
|
|
|
@ -17,12 +17,15 @@ package zerotier
|
|||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
CertificateSerialNoSize = 48
|
||||
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.
|
||||
|
@ -79,6 +82,8 @@ type Certificate struct {
|
|||
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 {
|
||||
C unsafe.Pointer
|
||||
internalCertificate C.ZT_Certificate
|
||||
|
@ -89,6 +94,65 @@ type CCertificate struct {
|
|||
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.
|
||||
func NewCertificateFromCCertificate(ccptr unsafe.Pointer) *Certificate {
|
||||
cc := (*C.ZT_Certificate)(ccptr)
|
||||
|
@ -349,3 +413,77 @@ func (c *Certificate) CCertificate() *CCertificate {
|
|||
|
||||
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"
|
||||
ErrUnrecognizedIdentityType Err = "unrecognized identity type"
|
||||
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
|
||||
|
|
Loading…
Add table
Reference in a new issue