Commit 56eb39c3 authored by Steven Saunders's avatar Steven Saunders Committed by Nathan Heldt-Sheller

Gen/Check certs against Bangkok requirements

Change-Id: I3ee0fb005c6c9ce91a0e4c16454b6856c508616f
Signed-off-by: default avatarSteven Saunders <s.saunders-contractor@cablelabs.com>
parent 8f3d2cf7
......@@ -67,6 +67,9 @@ proguard-project.txt
*.iml
.idea
# Ignore Project files for VS Code
.vscode/
# Ignore CTags default data
tags
......
......@@ -89,6 +89,31 @@ extern "C"
return; \
} \
/**
* Macro to verify an expression, goto exit: if not satisfied
*
* @param log_tag log tag
* @param expr Expression to verify.
* @param msg Message to log prior to exiting
* @param log_level logging level
*
* @note Invoking function must define "exit:" label for goto functionality to work correctly.
*/
#define VERIFY_SUCCESS_OR_EXIT(log_tag, expr, msg, log_level) do{ if (!(expr)) \
{OIC_LOG((log_level), (log_tag), (msg)); goto exit; } }while(0)
/**
* Macro to verify an expression, or return
*
* @param log_tag log tag
* @param expr Expression to verify.
* @param msg Message to log prior to exiting
* @param log_level logging level
*/
#define VERIFY_SUCCESS_OR_RETURN(log_tag, expr, msg, log_level) do{ if (!(expr)) \
{OIC_LOG((log_level), (log_tag), (msg)); return; } }while(0)
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
......
//******************************************************************
// Copyright 2018 Cable Television Laboratories, Inc.
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
/**
* @file
*
* This file contains common utility functions for validating OCF certificate profiles
*/
#ifndef CA_CERT_PROFILE_H_
#define CA_CERT_PROFILE_H_
#if defined(__WITH_DTLS__) || defined (__WITH_TLS__)
#include <mbedtls/x509_crt.h>
#ifdef __cplusplus
extern "C"
{
#endif
/**
* Certificate Types
*/
typedef enum
{
CERT_CA_ROOT = 0,
CERT_CA_INT,
CERT_EE
} CertType;
/**
* Cert profile return values
*/
typedef enum
{
CP_STATUS_OK = 0, /* Success */
CP_NO_EE_CERT, /* No end entity cert in cert chain */
CP_MUL_EE_CERTS, /* Multiple end entity certs in cert chain */
CP_PROFILE_VIOLATION, /* Certificate(s) don't satisfy OCF profile requirements */
CP_INVALID_CERT_INPUT, /* Certificate input is invalid (or null) */
CP_DATE_ERROR, /* Problem setting or reading certificate validity dates */
CP_BUF_TOO_SMALL, /* Supplied buffer is not long enough for desired operation */
CP_STATUS_FAILED = 255 /* Failure */
} CertProfileResult;
/**
* Return values for cert chain functions that return a counts or -1 on failure
*/
#define CP_INVALID_CERT_CHAIN -1
#define CP_INVALID_CERT_LIST -1
/**
* Macro to log an mbedtls error
* For mbedtls functions that return 0 as non-error
*
* @param log_tag log tag
* @param ret value returned by mbedtls call
* @param log_level logging level
* @param buf char* buffer for error string processing
* @param buf_size size of bug
*
* NOTE: you must include "mbedtls/error.h" in order to use this
*/
#define CP_LOG_MBED_ERROR(log_tag, ret, buf, buf_size, log_level) do{ if (0!=(ret)) { \
mbedtls_strerror((ret), (buf), (buf_size)); \
OIC_LOG_V((log_level), (log_tag), "mbedtls error: %s", (buf)); } }while(0)
/**
* Cert profile violation flags
*/
typedef unsigned long CertProfileViolations;
#define CP_NO_VIOLATIONS (0)
#define CP_PROCESSING_ERROR (1 << 1)
#define CP_INVALID_SIG_ALGORITHM (1 << 2)
#define CP_INVALID_VERSION (1 << 3)
#define CP_INVALID_PUB_KEY_ALGORITHM (1 << 4)
#define CP_INVALID_KEY_USAGE_MISSING (1 << 5)
#define CP_INVALID_KEY_USAGE_EXTRA (1 << 6)
#define CP_INVALID_BASIC_CONSTRAINTS_CA (1 << 7)
#define CP_INVALID_BASIC_CONSTRAINTS_PATH_LEN (1 << 8)
#define CP_INVALID_EKU_NO_SERVER_AUTH (1 << 9)
#define CP_INVALID_EKU_NO_CLIENT_AUTH (1 << 10)
#define CP_INVALID_EKU_NO_OCF_ID_OID (1 << 11)
#define CP_INVALID_EKU_INCLUDES_ANY (1 << 12)
#define CP_INVALID_ISSUER_SUBJ_MISMATCH (1 << 13)
#define CP_NOT_YET_VALID (1 << 14)
#define CP_EXPIRED (1 << 15)
/**
* Validate an end-entity certificate against OCF cert profile requirements
*
* @param[in] cert end entity cert to validate
*
* @return Success: CP_NO_VIOLATIONS
* Errors: 1 or more violation bits set
*/
CertProfileViolations ValidateEndEntityCertProfile(const mbedtls_x509_crt *cert);
/**
* Validate an intermediate-ca certificate against OCF cert profile requirements
*
* @param[in] cert end entity ca cert to validate
*
* @return Success: CP_NO_VIOLATIONS
* Errors: 1 or more violation bits set
*/
CertProfileViolations ValidateIntermediateCACertProfile(const mbedtls_x509_crt *cert);
/**
* Validate a root-ca certificate against OCF cert profile requirements
*
* @param[in] cert intermediate ca cert to validate
*
* @return Success: CP_NO_VIOLATIONS
* Errors: 1 or more violation bits set
*/
CertProfileViolations ValidateRootCACertProfile(const mbedtls_x509_crt *cert);
/**
* Validate a certificate's time window
*
* @param[in] cert cert to validate
*
* @return Success: CP_NO_VIOLATIONS
* Errors: CP_NOT_YET_VALID and/or CP_EXPIRED
*/
CertProfileViolations ValidateCertTimeWindow(const mbedtls_x509_crt *cert);
/**
* Given a cert chain intended for authentication, validated that the certs
* within the chain conform to OCF certificate profile requirements.
*
* @param[in] certChain cert chain intended for authentication
*
* @return Number of certs in the list that did not validate.
* 0 = success
* CP_INVALID_CERT_CHAIN for invalid input chain
* > 0 = some number of certs did not validate
*/
int ValidateAuthCertChainProfiles(const mbedtls_x509_crt *certChain);
/**
* Given a list of root CA certs, validated that the certs
* within the list conform to OCF certificate profile requirements.
*
* @param[in] certList Chain chain intended for authentication
*
* @return Number of certs in the list that did not validate.
* 0 = success
* CP_INVALID_CERT_LIST for invalid input cert list
* > 0 = some number of certs did not validate
*/
int ValidateRootCACertListProfiles(const mbedtls_x509_crt *certList);
/**
* Given a list of certificates, validate that they are currently
* within each certificate's valid time window
*
* @param[in] certList List of certs for which to check time window
*
* @return number of certs in the list that are NOT in valid time window relative to now
* 0 = success
* CP_INVALID_CERT_LIST for invalid input cert list
* > 0 = some number of certs did not validate
*/
int CheckCertListTimeWindows(const mbedtls_x509_crt *certList);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif // (__WITH_DTLS__) || defined (__WITH_TLS__)
#endif /* CA_CERT_PROFILE_H_ */
......@@ -49,7 +49,10 @@ connectivity_env.SConscript(root_dir + '/common/SConscript',
connectivity_env.SConscript(root_dir + '/util/SConscript',
exports='connectivity_env')
src_files = [File('adapter_util/caadapterutils.c'),]
src_files = [File(src) for src in (
'adapter_util/caadapterutils.c',
'adapter_util/cacertprofile.c'
)]
if (('BLE' in ca_transport) or ('ALL' in ca_transport)):
src_files.append(File('adapter_util/cafragmentation.c'))
......
......@@ -32,6 +32,7 @@
#include "ca_adapter_net_ssl.h"
#include "cacommon.h"
#include "caipinterface.h"
#include "cacertprofile.h"
#include "oic_malloc.h"
#include "experimental/ocrandom.h"
#include "experimental/byte_array.h"
......@@ -758,6 +759,19 @@ static int InitPKIX(CATransportAdapter_t adapter)
OIC_LOG_V(WARNING, NET_SSL_TAG, "Own certificate chain parsing error: %d certs failed to parse", errNum);
goto required;
}
ret = ValidateAuthCertChainProfiles(&g_caSslContext->crt);
if (CP_INVALID_CERT_CHAIN == ret)
{
OIC_LOG(ERROR, NET_SSL_TAG, "Invalid own cert chain");
goto required;
}
else if (0 != ret)
{
OIC_LOG_V(ERROR, NET_SSL_TAG, "%d certificate(s) in own cert chain do not satisfy OCF profile requirements", ret);
goto required;
}
ret = mbedtls_pk_parse_key(&g_caSslContext->pkey, pkiInfo.key.data, pkiInfo.key.len,
NULL, 0);
if (0 != ret)
......@@ -808,6 +822,24 @@ static int InitPKIX(CATransportAdapter_t adapter)
{
OIC_LOG_V(WARNING, NET_SSL_TAG, "CA chain parsing warning: %d certs failed to parse", errNum);
}
else
{
ret = ValidateRootCACertListProfiles(&g_caSslContext->ca);
if (CP_INVALID_CERT_LIST == ret)
{
OIC_LOG(ERROR, NET_SSL_TAG, "Invalid own CA cert chain");
OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
DeInitPkixInfo(&pkiInfo);
return -1;
}
else if (0 < ret )
{
OIC_LOG_V(ERROR, NET_SSL_TAG, "%d certificate(s) in own CA cert chain violate OCF Root CA cert profile requirements", ret);
OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
DeInitPkixInfo(&pkiInfo);
return -1;
}
}
ret = mbedtls_x509_crl_parse_der(&g_caSslContext->crl, pkiInfo.crl.data, pkiInfo.crl.len);
if(0 != ret)
......@@ -2204,6 +2236,22 @@ CAResult_t CAdecryptSsl(const CASecureEndpoint_t *sep, uint8_t *data, size_t dat
mbedtls_x509_crt *peerCert = peer->ssl.session_negotiate->peer_cert;
if (NULL != peerCert)
{
ret = ValidateAuthCertChainProfiles(peerCert);
if (CP_INVALID_CERT_CHAIN == ret)
{
oc_mutex_unlock(g_sslContextMutex);
OIC_LOG(ERROR, NET_SSL_TAG, "Invalid peer cert chain");
OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
return CA_STATUS_FAILED;
}
else if (0 != ret)
{
oc_mutex_unlock(g_sslContextMutex);
OIC_LOG_V(ERROR, NET_SSL_TAG, "%d certificate(s) in peer cert chain do not satisfy OCF profile requirements", ret);
OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
return CA_STATUS_FAILED;
}
ret = PeerCertExtractCN(peerCert);
if (CA_STATUS_OK != ret)
{
......
//******************************************************************
// Copyright 2018 Cable Television Laboratories, Inc.
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific lan guage governing permissions and
// limitations under the License.
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include <string.h>
#include <time.h>
#include <mbedtls/error.h>
#include <mbedtls/x509_crt.h>
#include <mbedtls/oid.h>
#include "cacommonutil.h"
#include "cacertprofile.h"
#include "experimental/logger.h"
#define TAG "OIC_CC_CERT_PROFILE"
// OCF Compliant ID Cert profiles
static const mbedtls_x509_crt_profile s_certProfile = {
MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA256), // MD algorithms
MBEDTLS_X509_ID_FLAG(MBEDTLS_PK_ECKEY) | // Allowed key types
MBEDTLS_X509_ID_FLAG(MBEDTLS_PK_ECDSA),
MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_SECP256R1), // EC curves
0 // RSA minimum key length - not used because we only use EC key pairs
};
// OID for ID certificates (1.3.6.1.4.1.44924.1.6) suitable for mbedTLS check
static const char s_ekuIdOid[] = MBEDTLS_OID_ISO_IDENTIFIED_ORG "\x06\x01\x04\x01\x82\xDE\x7C\x01\x06";
static const unsigned int s_eeKeyUsage = MBEDTLS_X509_KU_DIGITAL_SIGNATURE |
MBEDTLS_X509_KU_KEY_AGREEMENT;
static const unsigned int s_eeNonKeyUsage = MBEDTLS_X509_KU_NON_REPUDIATION |
MBEDTLS_X509_KU_DATA_ENCIPHERMENT |
MBEDTLS_X509_KU_KEY_ENCIPHERMENT |
MBEDTLS_X509_KU_KEY_CERT_SIGN |
MBEDTLS_X509_KU_CRL_SIGN |
MBEDTLS_X509_KU_ENCIPHER_ONLY |
MBEDTLS_X509_KU_DECIPHER_ONLY;
static const unsigned int s_caKeyUsage = MBEDTLS_X509_KU_KEY_CERT_SIGN |
MBEDTLS_X509_KU_CRL_SIGN;
static const unsigned int s_caNonKeyUsage = MBEDTLS_X509_KU_DIGITAL_SIGNATURE |
MBEDTLS_X509_KU_NON_REPUDIATION |
MBEDTLS_X509_KU_KEY_ENCIPHERMENT |
MBEDTLS_X509_KU_DATA_ENCIPHERMENT |
MBEDTLS_X509_KU_KEY_AGREEMENT |
MBEDTLS_X509_KU_ENCIPHER_ONLY |
MBEDTLS_X509_KU_DECIPHER_ONLY;
#if defined(__WITH_DTLS__) || defined (__WITH_TLS__)
static CertProfileResult FindEndEntityCert(const mbedtls_x509_crt *certChain, mbedtls_x509_crt const **eeCert)
{
*eeCert = NULL;
const mbedtls_x509_crt* curCert = certChain;
while (NULL != curCert)
{
if (0 == curCert->ca_istrue)
{
// first EE
if (NULL == *eeCert)
{
*eeCert = curCert;
}
// more than 1 EE is an error condition
else
{
*eeCert = NULL;
OIC_LOG(ERROR, TAG, "More than 1 end entity cert in chain");
return CP_MUL_EE_CERTS;
}
}
curCert = curCert->next;
}
if (NULL == *eeCert)
{
OIC_LOG(WARNING, TAG, "No end entity cert in chain");
return CP_NO_EE_CERT;
}
return CP_STATUS_OK;
}
static CertProfileResult CheckMdAlgorithm(const mbedtls_x509_crt_profile *profile, mbedtls_md_type_t mdAlgorithm)
{
if ((MBEDTLS_X509_ID_FLAG(mdAlgorithm) & profile->allowed_mds) != 0)
{
return CP_STATUS_OK;
}
return CP_STATUS_FAILED;
}
static CertProfileResult CheckPubKeyAlgorithm(const mbedtls_x509_crt_profile *profile, mbedtls_pk_type_t pkAlgorithm)
{
if ((MBEDTLS_X509_ID_FLAG(pkAlgorithm) & profile->allowed_pks) != 0)
{
return CP_STATUS_OK;
}
return CP_STATUS_FAILED;
}
// returns CP_STATUS_OK if pathlen is valid for cert type, othewise returns CP_STATUS_FAILED
static CertProfileResult CheckPathLen( CertType certType, int mbedMaxPathLen) {
// mbedtls max_pathlen behaviour:
// CA Cert: Expects RFC5280_val as encoding input
// Provides RFC5280_val+1 as decoding output, where 0 = not present
// EE Cert: Does not encode
// Provides 0 as decoding output
switch(certType)
{
case CERT_CA_ROOT:
return (0 == mbedMaxPathLen) ? CP_STATUS_OK : CP_STATUS_FAILED;
case CERT_CA_INT:
return (1 == mbedMaxPathLen) ? CP_STATUS_OK : CP_STATUS_FAILED;
case CERT_EE:
return (0 == mbedMaxPathLen) ? CP_STATUS_OK : CP_STATUS_FAILED;
default:
{
OIC_LOG(WARNING, TAG, "Unkown cert type for pathlen check");
return CP_STATUS_FAILED;
}
}
}
// Check all cert entries that are common across root-ca, intermediate-ca, and ee certs
static CertProfileViolations ValidateCommonCertProfileEntries(const mbedtls_x509_crt *cert) {
CertProfileResult cpResult = CP_STATUS_OK;
CertProfileViolations profileViolations = CP_NO_VIOLATIONS;
// Start with time windows
// notBefore: Required
// notAfter: Required
profileViolations = ValidateCertTimeWindow(cert);
if (CP_NO_VIOLATIONS != profileViolations)
{
OIC_LOG(ERROR, TAG, "Cert has expired or is not yet valid");
}
// signatureAlgorithm
// ecdsa-with-SHA256 (OID: 1.2.840.10045.4.3.2)
cpResult = CheckMdAlgorithm(&s_certProfile, cert->sig_md);
if (CP_STATUS_OK != cpResult)
{
OIC_LOG(ERROR, TAG, "Cert signature algorithm must be SHA256");
profileViolations |= CP_INVALID_SIG_ALGORITHM;
}
// Version: v3
// mbedTLS version 3 = x509 v3
if (3 != cert->version)
{
OIC_LOG(ERROR, TAG, "Cert is not x509 v3");
profileViolations |= CP_INVALID_VERSION;
}
// Subject Public Key Info
// id-ecPublicKey (OID: 1.2.840.10045.2.1) secp256r1 (OID:1.2.840.10045.3.1.7)
cpResult = CheckPubKeyAlgorithm(&s_certProfile, cert->sig_pk);
if (CP_STATUS_OK != cpResult)
{
OIC_LOG(ERROR, TAG, "Cert public key algorithm must be ECDSA");
profileViolations |= CP_INVALID_PUB_KEY_ALGORITHM;
}
// SerialNumber: SHALL be a positive integer, unique among all certificates issued by Root CA
// Not possible to validate SN uniqueness
// Issuer: SHALL match the Subject field of the issuing Root CA
// mbedTLS will check proper chaining during DTLS handshake
return profileViolations;
}
CertProfileViolations ValidateEndEntityCertProfile(const mbedtls_x509_crt *cert)
{
// OCF requirements exist for the following extensions, but w/o mbedTLS support
// * check for certificate policies, if present must be 1.3.6.1.4.1.51414.0.1.1
// * cRL Distributiojn Points
if (NULL == cert)
{
return CP_INVALID_CERT_INPUT;
}
int mbedRet = 0;
CertProfileResult cpResult = CP_STATUS_OK;
CertProfileViolations profileViolations = CP_NO_VIOLATIONS;
// Check all common entries first
profileViolations = ValidateCommonCertProfileEntries(cert);
// keyUsage (REQUIRED/Critical)
// digitalSignature (0) and keyAgreement(4) bits SHALL be the only bits enabled
mbedRet = mbedtls_x509_crt_check_key_usage(cert, s_eeKeyUsage);
if (0 != mbedRet)
{
OIC_LOG(ERROR, TAG, "End entity cert key usage must include digitalSignature & keyAgreement");
profileViolations |= CP_INVALID_KEY_USAGE_MISSING;
}
if (0 != (cert->key_usage & s_eeNonKeyUsage))
{
OIC_LOG(ERROR, TAG, "End entity cert key usage must not include usages other than digitalSignature & keyAgreement");
profileViolations |= CP_INVALID_KEY_USAGE_EXTRA;
}
// basicConstraints (OPTIONAL/Critical)
// cA = FALSE
if (1 == cert->ca_istrue)
{
OIC_LOG(ERROR, TAG, "End entity cert marked as CA cert");
profileViolations |= CP_INVALID_BASIC_CONSTRAINTS_CA;
}
// pathLenConstraint: not present
cpResult = CheckPathLen(CERT_EE, cert->max_pathlen);
if (CP_STATUS_OK != cpResult)
{
OIC_LOG(ERROR, TAG, "Invalid End entity max pathlen");
profileViolations |= CP_INVALID_BASIC_CONSTRAINTS_PATH_LEN;
}
// extendedKeyUsage (REQUIRED/Non-critical)
// Mandatory: serverAuthentication - 1.3.6.1.5.5.7.3.1
mbedRet = mbedtls_x509_crt_check_extended_key_usage(cert,
MBEDTLS_OID_SERVER_AUTH,
MBEDTLS_OID_SIZE(MBEDTLS_OID_SERVER_AUTH));
if (0 != mbedRet)
{
OIC_LOG(ERROR, TAG, "End entity cert extended key usage must include serverAuthentication");
profileViolations |= CP_INVALID_EKU_NO_SERVER_AUTH;
}
// extendedKeyUsage (REQUIRED/Non-critical)
// Mandatory: clientAuthentication - 1.3.6.1.5.5.7.3.2
mbedRet = mbedtls_x509_crt_check_extended_key_usage(cert,
MBEDTLS_OID_CLIENT_AUTH,
MBEDTLS_OID_SIZE(MBEDTLS_OID_CLIENT_AUTH));
if (0 != mbedRet)
{
OIC_LOG(ERROR, TAG, "End entity cert extended key usage must include clientAuthentication");
profileViolations |= CP_INVALID_EKU_NO_CLIENT_AUTH;
}
// extendedKeyUsage (REQUIRED/Non-critical)
// Mandatory: OCF Identity certificate - 1.3.6.1.4.1.44924.1.6
mbedRet = mbedtls_x509_crt_check_extended_key_usage(cert, s_ekuIdOid, MBEDTLS_OID_SIZE(s_ekuIdOid));
if (0 != mbedRet)
{
OIC_LOG(ERROR, TAG, "End entity cert extended key usage must include OCF ID OID (1.3.6.1.4.1.44924.1.6");
profileViolations |= CP_INVALID_EKU_NO_OCF_ID_OID;
}
// extendedKeyUsage (REQUIRED/Non-critical)
// CAs SHALL NOT issue certificates with the anyExtendedKeyUsage OID (2.5.29.37.0)
mbedRet = mbedtls_x509_crt_check_extended_key_usage(cert,
MBEDTLS_OID_ANY_EXTENDED_KEY_USAGE,
MBEDTLS_OID_SIZE(MBEDTLS_OID_ANY_EXTENDED_KEY_USAGE));
if (0 == mbedRet)
{
OIC_LOG(ERROR, TAG, "End entity cert extended key usage must not include anyExtendedKeyUsage");
profileViolations |= CP_INVALID_EKU_INCLUDES_ANY;
}
// subjectAlternativeName: No requirements for ID certs
return profileViolations;
}
CertProfileViolations ValidateIntermediateCACertProfile(const mbedtls_x509_crt *cert)
{
// OCF requirements exist for the following extensions, but w/o mbedTLS support
// * cRL Distribution Points