openHiTLS offers functions such as creating, configuring, and managing security protocol links based on transport-layer security protocol standards, with the main functional interfaces available in the protocol module. openHiTLS supports various protocol versions and features, including basic protocol handshake, key update, application-layer protocol negotiation, and server name indication.
Currently, openHiTLS supports the following protocol versions:
- TLS1.2: used for secure renegotiation, application-layer protocol negotiation, server name indication, and session resumption
- TLS1.3: used for key update, application-layer protocol negotiation, server name indication, and session resumption
- DTLS1.2: used for secure renegotiation, application-layer protocol negotiation, server name indication, and session resumption
- TLCP: used for secure renegotiation and session resumption
Configuration Item | Specifications |
---|---|
TLS version | TLS12 (0x0303u) DTLS12 (0xfefdu) |
Algorithm suite | TLS_RSA_WITH_AES_128_CBC_SHA (0x002F) TLS_DHE_DSS_WITH_AES_128_CBC_SHA (0x0032) TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x0033) TLS_DH_anon_WITH_AES_128_CBC_SHA (0x0034) TLS_RSA_WITH_AES_256_CBC_SHA (0x0035) TLS_DHE_DSS_WITH_AES_256_CBC_SHA (0x0038) TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x0039) TLS_DH_anon_WITH_AES_256_CBC_SHA (0x003A) TLS_RSA_WITH_AES_128_CBC_SHA256 (0x003C) TLS_RSA_WITH_AES_256_CBC_SHA256 (0x003D) TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 (0x0040) TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 (0x0067) TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 (0x006A) TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 (0x006B) TLS_DH_anon_WITH_AES_128_CBC_SHA256 (0x006C) TLS_DH_anon_WITH_AES_256_CBC_SHA256 (0x006D) TLS_PSK_WITH_AES_128_CBC_SHA (0x008C) TLS_PSK_WITH_AES_256_CBC_SHA (0x008D) TLS_DHE_PSK_WITH_AES_128_CBC_SHA (0x0090) TLS_DHE_PSK_WITH_AES_256_CBC_SHA (0x0091) TLS_RSA_PSK_WITH_AES_128_CBC_SHA (0x0094) TLS_RSA_PSK_WITH_AES_256_CBC_SHA (0x0095) TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009C) TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009D) TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (0x009E) TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (0x009F) TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 (0x00A2) TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 (0x00A3) TLS_DH_anon_WITH_AES_128_GCM_SHA256 (0x00A6) TLS_DH_anon_WITH_AES_256_GCM_SHA384 (0x00A7) TLS_PSK_WITH_AES_128_GCM_SHA256 (0x00A8) TLS_PSK_WITH_AES_256_GCM_SHA384 (0x00A9) TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 (0x00AA) TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 (0x00AB) TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 (0x00AC) TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 (0x00AD) TLS_PSK_WITH_AES_128_CBC_SHA256 (0x00AE) TLS_PSK_WITH_AES_256_CBC_SHA384 (0x00AF) TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 (0x00B2) TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 (0x00B3) TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 (0x00B6) TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 (0x00B7) TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xC009) TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xC00A) TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xC013) TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xC014) TLS_ECDH_anon_WITH_AES_128_CBC_SHA (0xC018) TLS_ECDH_anon_WITH_AES_256_CBC_SHA (0xC019) TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 (0xC023) TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 (0xC024) TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xC027) TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xC028) TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xC02B) TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xC02C) TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xC02F) TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xC030) TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA (0xC035) TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA (0xC036) TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 (0xC037) TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 (0xC038) TLS_RSA_WITH_AES_128_CCM (0xC09C) TLS_RSA_WITH_AES_256_CCM (0xC09D) TLS_DHE_RSA_WITH_AES_128_CCM (0xC09E) TLS_DHE_RSA_WITH_AES_256_CCM (0xC09F) TLS_RSA_WITH_AES_128_CCM_8 (0xC0A0) TLS_RSA_WITH_AES_256_CCM_8 (0xC0A1) TLS_PSK_WITH_AES_256_CCM (0xC0A5) TLS_DHE_PSK_WITH_AES_128_CCM (0xC0A6) TLS_DHE_PSK_WITH_AES_256_CCM (0xC0A7) TLS_ECDHE_ECDSA_WITH_AES_128_CCM (0xC0AC) TLS_ECDHE_ECDSA_WITH_AES_256_CCM (0xC0AD) TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xCCA8) TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (0xCCA9) TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xCCAA) TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 (0xCCAB) TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 (0xCCAC) TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 (0xCCAD) TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 (0xCCAE) TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256 (0xD001) TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384 (0xD002) TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256 (0xD005) |
EC dotted format | uncompressed (0) |
Elliptic curve | secp256r1 (23) secp384r1 (24) secp521r1 (25) brainpoolP256r1 (26) brainpoolP384r1 (27) brainpoolP512r1 (28) x25519 (29) |
Signature hash algorithm | dsa_sha256 (0x0402) dsa_sha384 (0x0502) dsa_sha512 (0x0602) rsa_pkcs1_sha256 (0x0401) rsa_pkcs1_sha384 (0x0501) rsa_pkcs1_sha512 (0x0601) ecdsa_secp256r1_sha256 (0x0403) ecdsa_secp384r1_sha384 (0x0503) ecdsa_secp521r1_sha512 (0x0603) rsa_pss_rsae_sha256 (0x0804) rsa_pss_rsae_sha384 (0x0805) rsa_pss_rsae_sha512 (0x0806) rsa_pss_pss_sha256 (0x0809) rsa_pss_pss_sha384 (0x080a) rsa_pss_pss_sha512 (0x080b) ed25519 (0x0807) |
Dual-ended verification | HITLS_CFG_SetClientVerifySupport (disabled by default) |
Blank client certificate | HITLS_CFG_SetNoClientCertSupport (disabled by default) |
Do not verify peer certificate | HITLS_CFG_SetVerifyNoneSupport (disabled by default) |
Renegotiation | HITLS_CFG_SetRenegotiationSupport (disabled by default) |
Verify client certificate only once | HITLS_CFG_SetClientOnceVerifySupport (disabled by default) |
Send handshake packets in a single flight | HITLS_CFG_SetFlightTransmitSwitch (disabled by default) |
Quiet shutdown mode | HITLS_CFG_SetQuietShutdown (disabled by default) |
Extend primary key | HITLS_CFG_SetExtenedMasterSecretSupport (enabled by default) |
Support sessionTicket | HITLS_CFG_SetSessionTicketSupport (enabled by default) |
Verify keyUsage | HITLS_CFG_SetCloseCheckKeyUsage (enabled by default) |
Auto-generate DH parameter | HITLS_CFG_SetDhAutoSupport (enabled by default) |
Configuration Item | Specifications |
---|---|
TLS version | TLS13 (0x0304u) |
Algorithm suite | TLS_AES_128_GCM_SHA256 (0x1301) TLS_AES_256_GCM_SHA384 (0x1302) TLS_CHACHA20_POLY1305_SHA256 (0x1303) TLS_AES_128_CCM_SHA256 (0x1304) TLS_AES_128_CCM_8_SHA256 (0x1305) |
EC dotted format | uncompressed (0) |
Elliptic curve | secp256r1 (23) secp384r1 (24) secp521r1 (25) x25519 (29) ffdhe2048 (256) ffdhe3072 (257) ffdhe4096 (258) ffdhe6144 (259) ffdhe8192 (260) |
Signature hash algorithm | rsa_pkcs1_sha256 (0x0401) rsa_pkcs1_sha384 (0x0501) rsa_pkcs1_sha512 (0x0601) ecdsa_secp256r1_sha256 (0x0403) ecdsa_secp384r1_sha384 (0x0503) ecdsa_secp521r1_sha512 (0x0603) rsa_pss_rsae_sha256 (0x0804) rsa_pss_rsae_sha384 (0x0805) rsa_pss_rsae_sha512 (0x0806) rsa_pss_pss_sha256 (0x0809) rsa_pss_pss_sha384 (0x080a) rsa_pss_pss_sha512 (0x080b) ed25519 (0x0807) |
Dual-ended verification | HITLS_CFG_SetClientVerifySupport (disabled by default) |
Blank client certificate | HITLS_CFG_SetNoClientCertSupport (disabled by default) |
Do not verify peer certificate | HITLS_CFG_SetVerifyNoneSupport (disabled by default) |
Verify client certificate only once | HITLS_CFG_SetClientOnceVerifySupport (disabled by default) |
Authentication after handshake | HITLS_CFG_SetPostHandshakeAuthSupport (disabled by default) |
Send handshake packets in a single flight | HITLS_CFG_SetFlightTransmitSwitch (disabled by default) |
Quiet shutdown mode | HITLS_CFG_SetQuietShutdown (disabled by default) |
Extend primary key | HITLS_CFG_SetExtenedMasterSecretSupport (enabled by default) |
Support sessionTicket | HITLS_CFG_SetSessionTicketSupport (enabled by default) |
Verify keyUsage | HITLS_CFG_SetCloseCheckKeyUsage (enabled by default) |
Auto-generate DH parameter | HITLS_CFG_SetDhAutoSupport (enabled by default) |
Configuration Item | Specifications |
---|---|
TLCP version | TLCP11 (0x0101u) |
Algorithm suite | ECDHE_SM4_CBC_SM3 (0xE011) ECC_SM4_CBC_SM3 (0xE013) |
EC dotted format | HITLS_POINT_FORMAT_UNCOMPRESSED (0) |
Elliptic curve | curveSM2 (41) |
Signature hash algorithm | sm2sig_sm3 (0x0708) |
Dual-ended verification | HITLS_CFG_SetClientVerifySupport (disabled by default) |
Blank client certificate | HITLS_CFG_SetNoClientCertSupport (disabled by default) |
Do not verify peer certificate | HITLS_CFG_SetVerifyNoneSupport (disabled by default) |
Verify client certificate only once | HITLS_CFG_SetClientOnceVerifySupport (disabled by default) |
Send handshake packets in a single flight | HITLS_CFG_SetFlightTransmitSwitch (disabled by default) |
Quiet shutdown mode | HITLS_CFG_SetQuietShutdown (disabled by default) |
Verify keyUsage | HITLS_CFG_SetCloseCheckKeyUsage (enabled by default) |
Name | DTLS1.2 | TLS1.2 | TLS1.3 | TLCP |
---|---|---|---|---|
server_name | Yes | Yes | Yes | No |
supported_groups | Yes | Yes | Yes | Yes |
ec_point_formats | Yes | Yes | No | Yes |
signature_algorithms | Yes | Yes | Yes | No |
application_layer_protocol_negotiation | Yes | Yes | Yes | No |
extended_master_secret | Yes | Yes | No | No |
session_ticket | Yes | Yes | No | No |
encrypt_then_mac | Yes | Yes | No | Yes |
renegotiation_info | Yes | Yes | No | Yes |
early_data | No | No | No | No |
supported_versions | No | No | Yes | No |
cookie | Yes | No | Yes | No |
pre_shared_key | No | No | Yes | No |
psk_key_exchange_modes | No | No | Yes | No |
certificate_authorities | No | No | No | No |
oid_filters | No | No | No | No |
post_handshake_auth | No | No | Yes | No |
signature_algorithms_cert | No | No | No | No |
key_share | No | No | Yes | No |
In openHiTLS, secure transmission context is split into two layers: HITLS_Config
and HITLS_Ctx
. HITLS_Config
is the configuration context, with one context for each service type (like client or server) in a process. HITLS_Ctx
is the link context, with one context for each connection. The configuration context and link context have a many-to-one relationship, and each link context in openHiTLS has a copy of the configuration context.
The protocol module cannot create file descriptions (FDs). Users must create and configure FDs in openHiTLS. Once openHiTLS reads and writes the FDs, users should close them. openHiTLS supports non-blocking I/O in both handshake and read/write phases. If calling HITLS_Read
or HITLS_Write
returns HITLS_REC_NORMAL_RECV_BUF_EMPTY
or HITLS_REC_NORMAL_IO_BUSY
, openHiTLS needs to repeat the read/write operation. In practice, the epoll/select driver is typically used to implement non-blocking I/O capabilities. The following is a piece of exemplary code of non-blocking I/O:
// Shake hands with the client.
do {
ret = HITLS_Connect(ctx);
} while (ret == HITLS_REC_NORMAL_RECV_BUF_EMPTY || ret == HITLS_REC_NORMAL_IO_BUSY);
// Shake hands with the server.
do {
ret = HITLS_Accept(ctx);
} while (ret == HITLS_REC_NORMAL_RECV_BUF_EMPTY || ret == HITLS_REC_NORMAL_IO_BUSY);
NOTE: The
do while
statement serves as a reference only. In practice, the service logic may be implemented in a different manner.
- The openHiTLS client and server are both authenticated using certificates.
- Users can quickly get started with openHiTLS using the default configurations provided. Typically, only a few additional configurations based on the defaults are required for openHiTLS to function properly. openHiTLS provides a rich set of configuration interfaces, and an API manual is provided to help product developers configure openHiTLS options as needed.
The dependency on certain algorithm implementations and certificate parsing varies depending on product requirements. As such, openHiTLS offers registration interfaces for products to register interfaces that meet their specific requirements. For details about the interfaces that openHiTLS depends on, see the following headers file of the API manual:
Registering algorithm-related callback functions:
/**
* @brief Callback functions that must be registered
*/
typedef struct {
CRYPT_RandBytesCallback randBytes; /**<; Obtain a random number. */
CRYPT_HmacSizeCallback hmacSize; /**<; HMAC: Obtain the HMAC length based on the hash algorithm. */
CRYPT_HmacInitCallback hmacInit; /**<; HMAC: Initialize the context. */
CRYPT_HmacFreeCallback hmacFree; /**<; HMAC: Release the context. */
CRYPT_HmacUpdateCallback hmacUpdate; /**<; HMAC: Add input data. */
CRYPT_HmacFinalCallback hmacFinal; /**<; HMAC: Output results. */
CRYPT_HmacCallback hmac; /**<; HMAC: Use the complete HMAC function. */
CRYPT_DigestSizeCallback digestSize; /**<; HASH: Obtain the hash length. */
CRYPT_DigestInitCallback digestInit; /**<; HASH: Initialize the context. */
CRYPT_DigestCopyCallback digestCopy; /**<; HASH: Copy the hash context. */
CRYPT_DigestFreeCallback digestFree; /**<; HASH: Release the context. */
CRYPT_DigestUpdateCallback digestUpdate; /**<; HASH: Add input data. */
CRYPT_DigestFinalCallback digestFinal; /**<; HASH: Output hash results. */
CRYPT_DigestCallback digest; /**<; HASH: Use the complete hash function. */
CRYPT_EncryptCallback encrypt; /**<; TLS encryption: Provide the encryption capability for the record layer. */
CRYPT_DecryptCallback decrypt; /**<; TLS decryption: Provide the decryption capability for the record layer. */
} HITLS_CRYPT_BaseMethod;
/**
* @brief Callback functions that need to be registered for ECDH
*/
typedef struct {
CRYPT_GenerateEcdhKeyPairCallback generateEcdhKeyPair; /**<; ECDH: Generate a key pair based on the elliptic curve parameters. */
CRYPT_FreeEcdhKeyCallback freeEcdhKey; /**<; ECDH: Release the elliptic curve key. */
CRYPT_GetEcdhEncodedPubKeyCallback getEcdhPubKey; /**<; ECDH: Extract public key data. */
CRYPT_CalcEcdhSharedSecretCallback calcEcdhSharedSecret; /**<; ECDH: Calculate the shared key based on the local key and peer public key data. */
} HITLS_CRYPT_EcdhMethod;
/**
* @brief Callback functions that need to be registered for DH
*/
typedef struct {
CRYPT_GenerateDhKeyBySecbitsCallback generateDhKeyBySecbits; /**<; DH: Generate a key pair based on `secbits`. */
CRYPT_GenerateDhKeyByParamsCallback generateDhKeyByParams; /**<; DH: Generate a key pair based on DH parameters. */
CRYPT_FreeDhKeyCallback freeDhKey; /**<; DH: Release the key. */
CRYPT_DHGetParametersCallback getDhParameters; /**<; DH: Leverage the key handle to obtain `p`, `g`, `plen`, and `glen`. */
CRYPT_GetDhEncodedPubKeyCallback getDhPubKey; /**<; DH: Extract public key data. */
CRYPT_CalcDhSharedSecretCallback calcDhSharedSecret; /**<; DH: Calculate the shared key based on the local key and peer public key data. */
} HITLS_CRYPT_DhMethod;
/**
* @brief Callback functions that need to be registered for KDF
*/
typedef struct {
CRYPT_HkdfExtractCallback hkdfExtract;
CRYPT_HkdfExpandCallback hkdfExpand;
} HITLS_CRYPT_KdfMethod;
/**
* @brief Register fundamental callback functions
*/
int32_t HITLS_CRYPT_RegisterBaseMethod(HITLS_CRYPT_BaseMethod *userCryptCallBack);
/**
* @brief Register ECDH callback functions
*/
int32_t HITLS_CRYPT_RegisterEcdhMethod(HITLS_CRYPT_EcdhMethod *userCryptCallBack);
/**
* @brief Register DH callback functions
*/
int32_t HITLS_CRYPT_RegisterDhMethod(const HITLS_CRYPT_DhMethod *userCryptCallBack);
/**
* @brief Register HKDF callback functions
*/
int32_t HITLS_CRYPT_RegisterHkdfMethod(HITLS_CRYPT_KdfMethod *userCryptCallBack);
Registering certificate-related callback functions:
/**
* @brief Callback functions that must be registered
*/
typedef struct {
CERT_StoreNewCallBack certStoreNew; /**< Create a certificate store. */
CERT_StoreDupCallBack certStoreDup; /**< Copy the certificate store. */
CERT_StoreFreeCallBack certStoreFree; /**< Release the certificate store. */
CERT_StoreCtrlCallBack certStoreCtrl; /**< Control the certificate store. */
CERT_BuildCertChainCallBack buildCertChain; /**< Build a certificate chain. */
CERT_VerifyCertChainCallBack verifyCertChain; /**< Verify the certificate chain. */
CERT_CertEncodeCallBack certEncode; /**< Encode certificates. */
CERT_CertParseCallBack certParse; /**< Decode certificates. */
CERT_CertDupCallBack certDup; /**< Deep-copy certificates. */
CERT_CertRefCallBack certRef; /**< Increase certificate references by 1. */
CERT_CertFreeCallBack certFree; /**< Release certificates. */
CERT_CertCtrlCallBack certCtrl; /**< Control certificates. */
CERT_KeyParseCallBack keyParse; /**< Parse keys. */
CERT_KeyDupCallBack keyDup; /**< Deep-copy keys. */
CERT_KeyFreeCallBack keyFree; /**< Release keys. */
CERT_KeyCtrlCallBack keyCtrl; /**< Control keys. */
CERT_CreateSignCallBack createSign; /**< Create a signature. */
CERT_VerifySignCallBack verifySign; /**< Verify the signature. */
CERT_EncryptCallBack encrypt; /**< Encrypt key exchange. */
CERT_DecryptCallBack decrypt; /**< Decrypt key exchange. */
CERT_CheckPrivateKeyCallBack checkPrivateKey; /**< Check whether the certificates and keys match. */
} HITLS_CERT_MgrMethod;
/**
* @brief Register certificate-related callback functions
*/
int32_t HITLS_CERT_RegisterMgrMethod(HITLS_CERT_MgrMethod *method);
/**
* @brief Deregister certificate-related callback functions
*/
void HITLS_CERT_DeinitMgrMethod(void);
To use the certificate authentication-based client, you need both a trust certificate pool and device certificates. The trust certificate pool specifies which certificate authorities the client trusts.
- The trust certificate pool specifies which certificate authorities the client trusts. It must be configured prior to connection establishment and will then be loaded into the certificate management engine. There are two types of trust certificate pools:
-
Pool used to verify the peer certificate chain When using algorithm suites that require server identity verification, the server sends certificates and the certificate chain to the TLS client through handshake messages. If the certificates and certificate chain are not issued by any authority trusted by the client, the client will send a critical alarm and terminate the handshake process. If no trust certificate pool is configured, certificate chain verification will fail, resulting in a TLS handshake failure.
For the configuration context, users can use the following interface to configure a trust certificate pool for verifying peer certificates:
/** * @brief Set `VerifyStore` for TLS to verify certificates. */ int32_t HITLS_CFG_SetVerifyStore(HITLS_Config *config, HITLS_CERT_Store *store, bool isClone);
For the link context, users can call
HITLS_SetVerifyStore
to setVerifyStore
.NOTE: Calling
HITLS_CFG_NewXXXConfig
will generate a default certificate pool,CertStore
. IfVerifyStore
is not set,CertStore
will be used to verify the certificate chain by default. -
Pool used to generate the local certificate chain As part of the handshake process, the server sends its local certificate to the peer for verification. If the certificate chain for the local certificate is not configured, the server will search the trust certificate pool for the chain and send it to the peer. If the server has sent a certificate chain, it can request the TLS client's certificate to verify the client's identity, which is known as two-way authentication. The TLS client will then send its local certificate and certificate chain to the server through handshake messages. If the local certificate is not found in the configured trust certificate pool or the pool is not configured, the client will send an empty certificate message. Whether the handshake can proceed depends on the server's behavior.
Users can use the following interface to configure a trust certificate pool for generating the local certificate chain:
/** * @brief Set the chain store used for TLS configuration to construct a certificate chain. */ int32_t HITLS_CFG_SetChainStore(HITLS_Config *config, HITLS_CERT_Store *store, bool isClone);
-
Using device certificates and the corresponding certificate chains: The server or client (in two-way authentication) needs to send device certificates and certificate chains to the peer. In addition to the trust certificate pools, the certificate chains can be added based on the device certificates. When certificate chains are sent to the peer, those that match the device certificates are preferred. You can use the following interfaces to add the desired certificate chains:
/** * @brief Add certificates to the certificate chain being used by **config**. */ int32_t HITLS_CFG_AddChainCert(HITLS_Config *config, HITLS_CERT_X509 *cert, bool isClone);
-
Adding certificates to the trust certificate pool: After configuring a trust certificate pool, you can add trust certificates to it through the following interface:
/** * @brief Add certificates to the specified trust certificate pool. */ int32_t HITLS_CFG_AddCertToStore(HITLS_Config *config, char *certPath, HITLS_CERT_StoreType storeType);
NOTE: This interface can be used to add certificates to the default certificate pool, verification certificate pool, and certificate chain pool. The certificates are transferred using relative paths.
A client certificate is a credential for client identity authentication. In two-way authentication, the TLS client will send its local certificate and certificate chain to the server through handshake messages. If no certificate is found in the client or no certificate is configured by users, the TLS client sends an empty certificate message. Whether the handshake can proceed depends on the server's behavior.
For the configuration context, users can use the following interfaces to configure client certificates:
/**
* @brief Add device certificates. Only one certificate of each type can be added.
*/
int32_t HITLS_CFG_SetCertificate(HITLS_Config *config, HITLS_CERT_X509 *cert, bool isClone);
/**
* @brief Load the device certificates from a file.
*/
int32_t HITLS_CFG_LoadCertFile(HITLS_Config *config, const uint8_t *file, HITLS_ParseFormat format);
/**
* @brief Read the device certificates from the buffer.
*/
int32_t HITLS_CFG_LoadCertBuffer(HITLS_Config *config, const uint8_t *buf, uint32_t bufLen, HITLS_ParseFormat format);
/**
* @brief Add SM device certificates. Only one certificate of each type can be added.
*/
int32_t HITLS_CFG_SetTlcpCertificate(HITLS_Config *config, HITLS_CERT_X509 *cert, bool isClone, bool isTlcpEncCert);
In addition to certificates, you need to configure private keys. Configuring certificates alone will also lead to handshake failure. You can use the following interfaces to configure private keys for certificates in the configuration context:
/**
* @brief Add private keys for device certificates. Only one private key can be added for each type of certificate.
*/
int32_t HITLS_CFG_SetPrivateKey(HITLS_Config *config, HITLS_CERT_Key *privateKey, bool isClone);
/**
* @brief Load the private keys of device certificates from a file.
*/
int32_t HITLS_CFG_LoadKeyFile(HITLS_Config *config, const uint8_t *file, HITLS_ParseFormat format);
/**
* @brief Read the private keys of device certificates from the buffer.
*/
int32_t HITLS_CFG_LoadKeyBuffer(HITLS_Config *config, const uint8_t *buf, uint32_t bufLen, HITLS_ParseFormat format);
/**
* @brief Add SM device certificates. Only one certificate of each type can be added.
*/
int32_t HITLS_CFG_SetTlcpCertificate(HITLS_Config *config, HITLS_CERT_X509 *cert, bool isClone, bool isTlcpEncCert);
You can use the following interface to delete all certificates and private keys:
/**
* @brief Release all loaded certificates and private keys.
*/
int32_t HITLS_CFG_RemoveCertAndKey(HITLS_Config *config);
In the link contexts that have been generated, you can use the following interface to delete the certificates and private keys:
/**
* @brief Release all loaded certificates and private keys.
*/
int32_t HITLS_RemoveCertAndKey(HITLS_Ctx *ctx);
NOTE: Each type of certificate and the corresponding private keys can be configured only once. If you try to configure them again, the previous configuration will be overwritten. Certificates of different types are not affected. For example, if two RSA certificates are configured in sequence, only the last one takes effect. If you configure an RSA certificate and an ECDSA certificate in sequence, both certificates take effect.
The procedure for establishing connections based on PSK negotiation is as follows:
- If PSK negotiation is used, the client sends a ClientHello message containing the PSK algorithm suite to the server, and the server determines whether to use the PSK algorithm suite.
- After a specific PSK algorithm suite is selected, the server includes
identity_hint
in the ServerKeyExchange message to indicate which PSK the client should use. - After receiving the ServerKeyExchange message containing
identity_hint
, the client requests the PSK and identity information from the TLS user through callback. - Then, the client includes
identity
in the ClientKeyExchange message to indicate which PSK the server should use. - After receiving the ClientKeyExchange message containing
identity
, the server requests the PSK from the upper-layer (the TLS user) through callback. - Then, the two ends generate a pre-master key based on the obtained PSK individually and clear the PSK. The PSK negotiation process is complete.
Therefore, the client using PSK authentication needs to set a pre-shared key to obtain the following callback information:
/**
* @brief Obtain the PSK prototype on the client.
*/
typedef uint32_t (*HITLS_PskClientCb)(HITLS_Ctx *ctx, const uint8_t *hint, uint8_t *identity, uint32_t maxIdentityLen, uint8_t *psk, uint32_t maxPskLen);
/**
* @brief Set the PSK callback on the client, which is used to obtain an identity and PSK during PSK negotiation.
*/
int32_t HITLS_CFG_SetPskClientCallback(HITLS_Config *config, HITLS_PskClientCb callback);
See client.c
Most code of PSK Authentication-based client is the same as that Certificate Authentication-based client, except for the configuration of HITLS_Config
.
...
uint32_t ExampleClientCb(HITLS_Ctx *ctx, const uint8_t *hint, uint8_t *identity, uint32_t maxIdentityLen, uint8_t *psk,
uint32_t maxPskLen)
{
(void)ctx;
(void)hint;
int32_t ret;
const char pskTrans[] = "psk data";
uint32_t pskTransUsedLen = sizeof(pskTransUsedLen);
if (memcpy_s(identity, maxIdentityLen, "hello", strlen("hello") + 1) != EOK) {
return 0;
}
if (memcpy_s(psk, maxPskLen, pskTrans, pskTransUsedLen) != EOK) {
return 0;
}
return pskTransUsedLen;
}
int main(int32_t argc, char *argv[])
{
...
config = HITLS_CFG_NewTLS12Config();
if (config == NULL) {
printf("HITLS_CFG_NewTLS12Config failed.\n");
return -1;
}
uint16_t cipherSuite = HITLS_PSK_WITH_AES_128_GCM_SHA256;
// config cipher suite
if (HITLS_CFG_SetCipherSuites(config, &cipherSuite, 1) != HITLS_SUCCESS) {
printf("HITLS_CFG_SetCipherSuites err\n");
return -1;
}
// config PSK callbacks
if (HITLS_CFG_SetPskClientCallback(config, (HITLS_PskClientCb)ExampleClientCb) != HITLS_SUCCESS) {
printf("HITLS_CFG_SetPskClientCallback err\n");
return -1;
}
ctx = HITLS_New(config);
if (ctx == NULL) {
printf("HITLS_New failed.\n");
goto exit;
}
...
}
The steps except for the following are the same as those described in "Certificate Authentication-based Client."
config = HITLS_CFG_NewTLCPConfig();
if (config == NULL) {
printf("HITLS_CFG_NewTLCPConfig failed.\n");
return -1;
}
uint16_t cipherSuite = HITLS_ECC_SM4_CBC_SM3;
// Configure the algorithm suite.
if (HITLS_CFG_SetCipherSuites(config, &cipherSuite, 1) != HITLS_SUCCESS) {
printf("HITLS_CFG_SetCipherSuites err\n");
return -1;
}
/* Load certificates. This capability needs to be implemented by users. */
HITLS_CFG_AddCertToStore(config, "rootCA.pem", TLS_CERT_STORE_TYPE_DEFAULT);
HITLS_CFG_AddCertToStore(config, "intCA.pem", TLS_CERT_STORE_TYPE_DEFAULT);
// In two-way authentication scenarios, load the signature certificate and private key from a file. This capability needs to be implemented by users.
HITLS_CERT_X509 *signCert = LoadCertFromFile("ClientSignCert.pem");
HITLS_CERT_X509 *signKey = LoadKeyFromFile("ClientSignKey.pem");
// Load the encryption certificate and private key from a file.
HITLS_CERT_X509 *encCert = LoadCertFromFile("ClientEncCert.pem");
HITLS_CERT_X509 *encKey = LoadKeyFromFile("ClientEncKey.pem");
//Add the SM signature certificate and private key.
HITLS_CFG_SetTlcpCertificate(config, signCert, false, false);
HITLS_CFG_SetTlcpPrivateKey(config, signKey, false, false);
//Add the SM encryption certificate and private key.
HITLS_CFG_SetTlcpCertificate(config, signCert, false, true);
HITLS_CFG_SetTlcpPrivateKey(config, signKey, false, true);
...
To use the certificate authentication-based TLS server, you need both a trust certificate pool and device certificates. The trust certificate pool specifies which certificate authorities the client trusts. A device certificate is a credential for server identity authentication. The server can determine whether to verify the client identity based on two-way authentication configuration items.
If the server has sent a certificate chain, it can request the TLS client's certificate to verify the client's identity, which is known as two-way authentication. openHiTLS provides the following configuration items:
- Two-way authentication
This function is disabled by default. That is, the server does not verify the client identity by default. You can control the function through the
HITLS_CFG_SetClientVerifySupport
interface.
/**
* @brief Set whether to verify the client certificate.
This setting has no impact on the client.
The server sends a certificate request.
*/
int32_t HITLS_CFG_SetClientVerifySupport(HITLS_Config *config, bool support);
- Acceptance of no client certificates
This setting takes effect only when two-way authentication is enabled. It is disabled by default, meaning that the TLS server must verify the client certificate. If the certificate chain sent by the client is empty or fails verification, the TLS server sends a critical alarm and terminates the handshake.
You can control the function through the
HITLS_CFG_SetNoClientCertSupport
interface.
/**
* @brief Set whether to accept the scenario without any client certificates. This setting takes effect only when client certificate verification is enabled.
This setting has no impact on the client.
The server checks whether the certificate verification is successful when receiving an empty certificate from the client. The verification fails by default.
*/
int32_t HITLS_CFG_SetNoClientCertSupport(HITLS_Config *config, bool support);
Refer to "Loading Trust Certificates."
If the algorithm suite requires server identity verification, users need to configure the server certificates, certificate chain, and private keys. Refer to "Configuring Client Certificates."
The callback for the PSK authentication-based server to obtain the pre-shared key is slightly different from that used by the client, as shown below:
/**
* @brief Server PSK negotiation callback
*/
typedef int32_t (*HITLS_PskFindSessionCb)(HITLS_Ctx *ctx, const uint8_t *identity, uint32_t identityLen,
HITLS_Session **session);
/**
* @brief Set the callback for the PSK authentication-based server, which is used to obtain a PSK during PSK negotiation.
*/
int32_t HITLS_CFG_SetPskServerCallback(HITLS_Config *config, HITLS_PskServerCb callback);
For details about the remaining procedure, see "PSK Authentication-based Client."
See server.c
Most code of PSK Authentication-based server is the same as that Certificate Authentication-based server, except for the configuration of HITLS_Config
.
...
uint32_t ExampleServerCb(HITLS_Ctx *ctx, const uint8_t *identity, uint8_t *psk, uint32_t maxPskLen)
{
(void)ctx;
if (identity == NULL || strcmp((const char *)identity, "hello") != 0) {
return 0;
}
const char pskTrans[] = "psk data";
uint32_t pskTransUsedLen = sizeof(pskTransUsedLen);
if (memcpy_s(psk, maxPskLen, pskTrans, pskTransUsedLen) != EOK) {
return 0;
}
return pskTransUsedLen;
}
int main(int32_t argc, char *argv[])
{
...
config = HITLS_CFG_NewTLS12Config();
if (config == NULL) {
printf("HITLS_CFG_NewTLS12Config failed.\n");
return -1;
}
uint16_t cipherSuite = HITLS_PSK_WITH_AES_128_GCM_SHA256;
// config cipher suite
if (HITLS_CFG_SetCipherSuites(config, &cipherSuite, 1) != HITLS_SUCCESS) {
printf("HITLS_CFG_SetCipherSuites err\n");
return -1;
}
// config PSK callback
if (HITLS_CFG_SetPskServerCallback(tlsConfig, (HITLS_PskServerCb)ExampleServerCb) != HITLS_SUCCESS) {
printf("HITLS_CFG_SetPskClientCallback err\n");
return -1;
}
ctx = HITLS_New(config);
if (ctx == NULL) {
printf("HITLS_New failed.\n");
goto exit;
}
...
}
The steps except for the following are the same as those described in "Certificate Authentication-based Server."
...
config = HITLS_CFG_NewTLCPConfig();
if (cfg == NULL) {
printf("HITLS_CFG_NewTLCPConfig failed.\n");
return -1;
}
uint16_t cipherSuite = HITLS_ECC_SM4_CBC_SM3;
// Configure the algorithm suite.
if (HITLS_CFG_SetCipherSuites(config, &cipherSuite, 1) != HITLS_SUCCESS) {
printf("HITLS_CFG_SetCipherSuites err\n");
return -1;
}
if (HITLS_CFG_SetClientVerifySupport(config, ture) != HITLS_SUCCESS) {
printf("HITLS_CFG_SetClientVerifySupport err\n");
return -1;
}
/* Load certificates. This capability needs to be implemented by users. */
HITLS_CFG_AddCertToStore(config, "rootCA.pem", TLS_CERT_STORE_TYPE_DEFAULT);
HITLS_CFG_AddCertToStore(config, "intCA.pem", TLS_CERT_STORE_TYPE_DEFAULT);
// Load the signature certificate and private key from a file. This capability needs to be implemented by users.
HITLS_CERT_X509 *signCert = LoadCertFromFile("ServerSignCert.pem");
HITLS_CERT_X509 *signKey = LoadKeyFromFile("ServerSignKey.pem");
// Load the encryption certificate and private key from a file.
HITLS_CERT_X509 *encCert = LoadCertFromFile("ServerEncCert.pem");
HITLS_CERT_X509 *encKey = LoadKeyFromFile("ServerEncKey.pem");
//Add the SM signature certificate and private key.
HITLS_CFG_SetTlcpCertificate(config, signCert, false, false);
HITLS_CFG_SetTlcpPrivateKey(config, signKey, false, false);
//Add the SM encryption certificate and private key.
HITLS_CFG_SetTlcpCertificate(config, signCert, false, true);
HITLS_CFG_SetTlcpPrivateKey(config, signKey, false, true);
...
TLS1.2/TLCP or DTLS1.2/TLCP supports security renegotiation. The renegotiation function enables the client or server to initiate a new negotiation over the same security connection to generate a new key. This function applies to connections that require high confidentiality and transmit a large amount of data. The security renegotiation procedure is as follows:
NOTE: Users can enter the renegotiation state through the
HITLS_Renegotiate
interface and trigger renegotiation handshakes through theHITLS_Accept
,HITLS_Connect
,HITLS_Write
, orHITLS_Read
interface. TheHITLS_Accept
andHITLS_Connect
interfaces are recommended.
Client example
/* Exchange data at the application layer. */
const uint8_t sndBuf[] = "Hi, this is client\n";
ret = HITLS_Write(ctx, sndBuf, sizeof(sndBuf));
if (ret != HITLS_SUCCESS) {
printf("HITLS_Write error:error code:%d\n", ret);
goto exit;
}
uint8_t readBuf[HTTP_BUF_MAXLEN + 1] = {0};
uint32_t readLen = 0;
ret = HITLS_Read(ctx, readBuf, HTTP_BUF_MAXLEN, &readLen);
if (ret != HITLS_SUCCESS) {
printf("HITLS_Read failed, ret = 0x%x.\n", ret);
goto exit;
}
/* The client enters the renegotiation state. */
ret = HITLS_Renegotiate(ctx);
if (ret != HITLS_SUCCESS) {
printf("HITLS_Renegotiate error:error code:%d\n", ret);
goto exit;
}
/* The client initiates a handshake, and the server processes the handshake through the `HITLS_Read` interface. */
ret = HITLS_Connect(ctx);
if (ret != HITLS_SUCCESS) {
printf("HITLS_Connect failed, ret = 0x%x.\n", ret);
goto exit;
}
/* The renegotiation is complete, and the data exchange at the application layer proceeds. */
Server example
/* Exchange data at the application layer. */
uint8_t readBuf[HTTP_BUF_MAXLEN + 1] = {0};
uint32_t readLen = 0;
ret = HITLS_Read(ctx, readBuf, HTTP_BUF_MAXLEN, &readLen);
if (ret != HITLS_SUCCESS) {
printf("HITLS_Read failed, ret = 0x%x.\n", ret);
goto exit;
}
const uint8_t sndBuf[] = "Hi, this is server\n";
ret = HITLS_Write(ctx, sndBuf, sizeof(sndBuf));
if (ret != HITLS_SUCCESS) {
printf("HITLS_Write error:error code:%d\n", ret);
goto exit;
}
/* The server enters the renegotiation state. */
ret = HITLS_Renegotiate(ctx);
if (ret != HITLS_SUCCESS) {
printf("HITLS_Renegotiate error:error code:%d\n", ret);
goto exit;
}
/* The server initiates a handshake, and the client processes the handshake through the `HITLS_Read` interface. */
ret = HITLS_Accept(ctx);
if (ret != HITLS_SUCCESS) {
printf("HITLS_Accept failed, ret = 0x%x.\n", ret);
goto exit;
}
/* The renegotiation is complete, and the data exchange at the application layer proceeds. */
TLS1.3 supports key update after connection establishment. The involved functions are as follows:
/**
* @brief Set the `KeyUpdate` type and send a `KeyUpdate` message to the peer.
*/
int32_t HITLS_KeyUpdate(HITLS_Ctx *ctx, uint32_t updateType);
The following KeyUpdate
types are supported:
The HITLS_UPDATE_NOT_REQUESTED = 0, // The peer does not have to reply to the `KeyUpdate` message.
HITLS_UPDATE_REQUESTED = 1, // The peer must reply to the `KeyUpdate` message.
Client example
/* Exchange data at the application layer. */
uint8_t readBuf[HTTP_BUF_MAXLEN + 1] = {0};
uint32_t readLen = 0;
ret = HITLS_Read(ctx, readBuf, HTTP_BUF_MAXLEN, &readLen);
if (ret != HITLS_SUCCESS) {
printf("HITLS_Read failed, ret = 0x%x.\n", ret);
goto exit;
}
const uint8_t sndBuf[] = "Hi, this is server\n";
ret = HITLS_Write(ctx, sndBuf, sizeof(sndBuf));
if (ret != HITLS_SUCCESS) {
printf("HITLS_Write error:error code:%d\n", ret);
goto exit;
}
/* The client initiates a `KeyUpdate` message that does not require replies from the peer. The peer processes the message through the `HITLS_Read` interface. */
ret = HITLS_KeyUpdate(ctx, HITLS_UPDATE_NOT_REQUESTED);
if (ret != HITLS_SUCCESS) {
printf("HITLS_KeyUpdate error:error code:%d\n", ret);
goto exit;
}
/* The key update process is complete. */
Server example
/* Exchange data at the application layer. */
uint8_t readBuf[HTTP_BUF_MAXLEN + 1] = {0};
uint32_t readLen = 0;
ret = HITLS_Read(ctx, readBuf, HTTP_BUF_MAXLEN, &readLen);
if (ret != HITLS_SUCCESS) {
printf("HITLS_Read failed, ret = 0x%x.\n", ret);
goto exit;
}
const uint8_t sndBuf[] = "Hi, this is server\n";
ret = HITLS_Write(ctx, sndBuf, sizeof(sndBuf));
if (ret != HITLS_SUCCESS) {
printf("HITLS_Write error:error code:%d\n", ret);
goto exit;
}
/* The server initiates a `KeyUpdate` message that requires replies from the peer. The peer processes the message through the `HITLS_Read` interface and returns replies to the `KeyUpdate` message. */
ret = HITLS_KeyUpdate(ctx, HITLS_UPDATE_REQUESTED);
if (ret != HITLS_SUCCESS) {
printf("HITLS_KeyUpdate error:error code:%d\n", ret);
goto exit;
}
/* The `HITLS_Read` interface receives the peer's replies to the `KeyUpdate` message. */
ret = HITLS_Read(ctx, readBuf, HTTP_BUF_MAXLEN, &readLen);
if (ret != HITLS_SUCCESS) {
printf("HITLS_Read failed, ret = 0x%x.\n", ret);
goto exit;
}
/* The key update process is complete. */