Skip to content

Latest commit

 

History

History
820 lines (673 loc) · 42.7 KB

2_Secure Communication Application Development Guide.md

File metadata and controls

820 lines (673 loc) · 42.7 KB

TLS Feature Introduction

Protocol Description

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

TLS/DTLS1.2 Specifications

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)

TLS1.3 Specifications

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)

TLCP Specifications

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)

Extended Capabilities

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

Framework

image

Context Overview

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.

Non-blocking I/O Capability

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.

Constraints

  1. The openHiTLS client and server are both authenticated using certificates.
  2. 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.

Dependencies

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;      /**&lt;; ECDH: Generate a key pair based on the elliptic curve parameters. */
    CRYPT_FreeEcdhKeyCallback freeEcdhKey;                      /**&lt;; ECDH: Release the elliptic curve key. */
    CRYPT_GetEcdhEncodedPubKeyCallback getEcdhPubKey;           /**&lt;; ECDH: Extract public key data. */
    CRYPT_CalcEcdhSharedSecretCallback calcEcdhSharedSecret;    /**&lt;; 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;    /**&lt;; DH: Generate a key pair based on `secbits`. */
    CRYPT_GenerateDhKeyByParamsCallback generateDhKeyByParams;      /**&lt;; DH: Generate a key pair based on DH parameters. */
    CRYPT_FreeDhKeyCallback freeDhKey;                              /**&lt;; DH: Release the key. */
    CRYPT_DHGetParametersCallback getDhParameters;                  /**&lt;; DH: Leverage the key handle to obtain `p`, `g`, `plen`, and `glen`. */
    CRYPT_GetDhEncodedPubKeyCallback getDhPubKey;                   /**&lt;; DH: Extract public key data. */
    CRYPT_CalcDhSharedSecretCallback calcDhSharedSecret;            /**&lt;; 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;             /**&lt; Encode certificates. */
    CERT_CertParseCallBack certParse;               /**&lt; Decode certificates. */
    CERT_CertDupCallBack certDup;                   /**&lt; Deep-copy certificates. */
    CERT_CertRefCallBack certRef;                   /**&lt; Increase certificate references by 1. */
    CERT_CertFreeCallBack certFree;                 /**&lt; Release certificates. */
    CERT_CertCtrlCallBack certCtrl;                 /**&lt; Control certificates. */

    CERT_KeyParseCallBack keyParse;                 /**&lt; Parse keys. */
    CERT_KeyDupCallBack keyDup;                     /**&lt; Deep-copy keys. */
    CERT_KeyFreeCallBack keyFree;                   /**&lt; Release keys. */
    CERT_KeyCtrlCallBack keyCtrl;                   /**&lt; Control keys. */
    CERT_CreateSignCallBack createSign;             /**&lt; Create a signature. */
    CERT_VerifySignCallBack verifySign;             /**&lt; Verify the signature. */
    CERT_EncryptCallBack encrypt;                   /**&lt; Encrypt key exchange. */
    CERT_DecryptCallBack decrypt;                   /**&lt; Decrypt key exchange. */

    CERT_CheckPrivateKeyCallBack checkPrivateKey;   /**&lt; 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);

Time Sequence Interaction of Secure Communication Applications

image

Example TLS Client

Client Type

Certificate Authentication-based Client

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.

Loading Trust Certificates

  • 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:
  1. 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 set VerifyStore.

    NOTE: Calling HITLS_CFG_NewXXXConfig will generate a default certificate pool, CertStore. If VerifyStore is not set, CertStore will be used to verify the certificate chain by default.

  2. 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.

Configuring Client Certificates

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.

PSK Authentication-based Client

The procedure for establishing connections based on PSK negotiation is as follows:

image

  1. 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.
  2. 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.
  3. After receiving the ServerKeyExchange message containing identity_hint, the client requests the PSK and identity information from the TLS user through callback.
  4. Then, the client includes identity in the ClientKeyExchange message to indicate which PSK the server should use.
  5. After receiving the ClientKeyExchange message containing identity, the server requests the PSK from the upper-layer (the TLS user) through callback.
  6. 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);

Sample Code

Certificate Authentication-based Client

See client.c

PSK Authentication-based Client

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; 
    } 

    ...
}

TLCP Client

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);
...

Example TLS Server

Server Type

Certificate Authentication-based Server

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.

Configuring the Two-Way Authentication Server

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:

  1. 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);
  1. 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);

Loading Trust Certificate Pools

Refer to "Loading Trust Certificates."

Configuring Server 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."

PSK Authentication-based Server

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."

Sample Code

Certificate Authentication-based Server

See server.c

### PSK Authentication-based Server

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; 
    } 

    ...
}

TLCP Server

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);
...

Example of TLS Session Key Update

Update Type

TLS1.2/TLCP or DTLS1.2/TLCP Renegotiation Example

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:

image

NOTE: Users can enter the renegotiation state through the HITLS_Renegotiate interface and trigger renegotiation handshakes through the HITLS_Accept, HITLS_Connect, HITLS_Write, or HITLS_Read interface. The HITLS_Accept and HITLS_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. */

Example of TLS1.3 Key Update

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. */