mirror of https://github.com/wolfSSL/wolfTPM.git
wolfTPM v3.4.0 release.
* Fixes for building without wolfCrypt or heap. * Fix for building wolfTPM DLL (was missing tbs.lib). * Fix for `wolfTPM2_New` to provide default TPM2 HAL IO callback.pull/362/head
parent
b36f792416
commit
95ae2f7459
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
project(wolfTPM VERSION 3.2.0 LANGUAGES C)
|
project(wolfTPM VERSION 3.4.0 LANGUAGES C)
|
||||||
|
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
set(WOLFTPM_DEFINITIONS)
|
set(WOLFTPM_DEFINITIONS)
|
||||||
|
|
27
ChangeLog.md
27
ChangeLog.md
|
@ -1,5 +1,32 @@
|
||||||
# Release Notes
|
# Release Notes
|
||||||
|
|
||||||
|
## wolfTPM Release 3.4.0 (July 30, 2024)
|
||||||
|
|
||||||
|
**Summary**
|
||||||
|
|
||||||
|
Added Endorsement Key Certificate support. Added support for NV read/write with policy. Added policy password support. Refactor of the session authentication structures.
|
||||||
|
|
||||||
|
**Detail**
|
||||||
|
|
||||||
|
* Added EK Certificate Support (PR #360)
|
||||||
|
- Added new API's `wolfTPM2_GetKeyTemplate_EK` and `wolfTPM2_GetKeyTemplate_EK` for getting EK public templates used for generating the EK primary key.
|
||||||
|
- Added `examples/endorsement/get_ek_certs` for showing how to retrieve and validate the manufacturers endorsement key certificates.
|
||||||
|
* Improvements to auth handling to support Policy Password and Policy Auth Value (PR #350)
|
||||||
|
- Refactor to eliminate confusing cast between TPMS_AUTH_COMMAND and TPM2_AUTH_SESSION.
|
||||||
|
- Support for policy auth value and policy password.
|
||||||
|
- Add new NV policy write/read API's `wolfTPM2_NVWriteAuthPolicy` and `wolfTPM2_NVReadAuthPolicy`.
|
||||||
|
* Fixed ST33KTPM IAK/IDevID provisioning NV indexes. (PR #361)
|
||||||
|
* Fixed TLS example build issues with wolfSSL not having crypto callback or PK callback enabled. (PR #360)
|
||||||
|
* Fixed CSR version (use version 0) (PR #359)
|
||||||
|
* Fixed issue with Doxygen generation of wolfTPM due to doxybook2 crashing on unnamed enum. (PR #357)
|
||||||
|
* Fixed HMAC session save last (not typically used) (PR #355)
|
||||||
|
* Fixed Infineon I2C HAL gating logic (PR #347)
|
||||||
|
* Added documentation for IAK/IDevID build options. (PR #361)
|
||||||
|
* Added support for Espressif IDE (see IDE/Espressif) (PR #321)
|
||||||
|
* Added tests for create_primary (PR #345)
|
||||||
|
* Improved software TPM (docs/SWTPM.md) documentation (PR #348)
|
||||||
|
|
||||||
|
|
||||||
## wolfTPM Release 3.2.0 (Apr 24, 2024)
|
## wolfTPM Release 3.2.0 (Apr 24, 2024)
|
||||||
|
|
||||||
**Summary**
|
**Summary**
|
||||||
|
|
|
@ -46,6 +46,7 @@ extern "C" {
|
||||||
/* TPM */
|
/* TPM */
|
||||||
#define WOLFSSL_AES_CFB /* required for parameter encryption */
|
#define WOLFSSL_AES_CFB /* required for parameter encryption */
|
||||||
#define WOLFSSL_PUBLIC_MP /* expose mp_ math functions - required for tpm ECC secret encrypt */
|
#define WOLFSSL_PUBLIC_MP /* expose mp_ math functions - required for tpm ECC secret encrypt */
|
||||||
|
#define WOLFTPM_AUTODETECT /* support any TPM model (unknown/safe options) */
|
||||||
|
|
||||||
/* Callbacks */
|
/* Callbacks */
|
||||||
#define WOLF_CRYPTO_CB
|
#define WOLF_CRYPTO_CB
|
||||||
|
|
|
@ -212,6 +212,7 @@
|
||||||
<SubSystem>
|
<SubSystem>
|
||||||
</SubSystem>
|
</SubSystem>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalDependencies>tbs.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
@ -254,6 +255,7 @@
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalDependencies>tbs.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
@ -289,6 +291,7 @@
|
||||||
<SubSystem>
|
<SubSystem>
|
||||||
</SubSystem>
|
</SubSystem>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalDependencies>tbs.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
@ -329,6 +332,7 @@
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
|
<GenerateDebugInformation>DebugFull</GenerateDebugInformation>
|
||||||
|
<AdditionalDependencies>tbs.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
# wolftpm
|
# wolftpm
|
||||||
# Copyright (C) 2021 wolfSSL Inc.
|
# Copyright (C) 2024 wolfSSL Inc.
|
||||||
# All right reserved.
|
# All right reserved.
|
||||||
|
|
||||||
AC_COPYRIGHT([Copyright (C) 2014-2021 wolfSSL Inc.])
|
AC_COPYRIGHT([Copyright (C) 2014-2024 wolfSSL Inc.])
|
||||||
AC_INIT([wolftpm],[3.2.0],[https://github.com/wolfssl/wolfTPM/issues],[wolftpm],[http://www.wolfssl.com])
|
AC_INIT([wolftpm],[3.4.0],[https://github.com/wolfssl/wolfTPM/issues],[wolftpm],[http://www.wolfssl.com])
|
||||||
|
|
||||||
AC_PREREQ([2.63])
|
AC_PREREQ([2.63])
|
||||||
AC_CONFIG_AUX_DIR([build-aux])
|
AC_CONFIG_AUX_DIR([build-aux])
|
||||||
|
@ -28,7 +28,7 @@ AC_ARG_PROGRAM
|
||||||
|
|
||||||
AC_CONFIG_HEADERS([src/config.h])
|
AC_CONFIG_HEADERS([src/config.h])
|
||||||
|
|
||||||
WOLFTPM_LIBRARY_VERSION=16:2:0
|
WOLFTPM_LIBRARY_VERSION=16:3:0
|
||||||
# | | |
|
# | | |
|
||||||
# +------+ | +---+
|
# +------+ | +---+
|
||||||
# | | |
|
# | | |
|
||||||
|
|
|
@ -38,7 +38,9 @@
|
||||||
|
|
||||||
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
||||||
#include <wolfssl/wolfcrypt/asn.h>
|
#include <wolfssl/wolfcrypt/asn.h>
|
||||||
|
#if !defined(WOLFCRYPT_ONLY)
|
||||||
#include "trusted_certs.h"
|
#include "trusted_certs.h"
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -89,7 +91,7 @@ static void show_ek_public(const TPM2B_PUBLIC* pub)
|
||||||
}
|
}
|
||||||
else if (pub->publicArea.type == TPM_ALG_ECC) {
|
else if (pub->publicArea.type == TPM_ALG_ECC) {
|
||||||
const char* curveName = "NULL";
|
const char* curveName = "NULL";
|
||||||
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC)
|
||||||
curveName = wc_ecc_get_name(
|
curveName = wc_ecc_get_name(
|
||||||
TPM2_GetWolfCurve(pub->publicArea.parameters.eccDetail.curveID));
|
TPM2_GetWolfCurve(pub->publicArea.parameters.eccDetail.curveID));
|
||||||
#endif
|
#endif
|
||||||
|
@ -153,8 +155,8 @@ int TPM2_EndorsementCert_Example(void* userCtx, int argc, char *argv[])
|
||||||
TPMT_PUBLIC publicTemplate;
|
TPMT_PUBLIC publicTemplate;
|
||||||
word32 nvIndex;
|
word32 nvIndex;
|
||||||
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
#ifndef WOLFTPM2_NO_WOLFCRYPT
|
||||||
int i;
|
|
||||||
#ifndef WOLFCRYPT_ONLY
|
#ifndef WOLFCRYPT_ONLY
|
||||||
|
int i;
|
||||||
WOLFSSL_CERT_MANAGER* cm = NULL;
|
WOLFSSL_CERT_MANAGER* cm = NULL;
|
||||||
#endif
|
#endif
|
||||||
DecodedCert cert;
|
DecodedCert cert;
|
||||||
|
@ -351,9 +353,10 @@ int TPM2_EndorsementCert_Example(void* userCtx, int argc, char *argv[])
|
||||||
rc = wc_DerToPem(certBuf, certSz, NULL, 0, CERT_TYPE);
|
rc = wc_DerToPem(certBuf, certSz, NULL, 0, CERT_TYPE);
|
||||||
if (rc > 0) { /* returns actual PEM size */
|
if (rc > 0) { /* returns actual PEM size */
|
||||||
pemSz = (word32)rc;
|
pemSz = (word32)rc;
|
||||||
rc = 0;
|
|
||||||
|
|
||||||
pemSz++; /* for '\0'*/
|
pemSz++; /* for '\0'*/
|
||||||
|
rc = 0;
|
||||||
|
}
|
||||||
|
if (rc == 0) {
|
||||||
pem = (char*)XMALLOC(pemSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
pem = (char*)XMALLOC(pemSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||||
if (pem == NULL) {
|
if (pem == NULL) {
|
||||||
rc = MEMORY_E;
|
rc = MEMORY_E;
|
||||||
|
|
|
@ -88,8 +88,13 @@ int TPM2_ExternalImport_Example(void* userCtx, int argc, char *argv[])
|
||||||
WOLFTPM2_DEV dev;
|
WOLFTPM2_DEV dev;
|
||||||
WOLFTPM2_KEY storage; /* SRK */
|
WOLFTPM2_KEY storage; /* SRK */
|
||||||
WOLFTPM2_KEY *primary;
|
WOLFTPM2_KEY *primary;
|
||||||
|
#ifndef WOLFTPM2_NO_HEAP
|
||||||
WOLFTPM2_KEYBLOB* key2;
|
WOLFTPM2_KEYBLOB* key2;
|
||||||
WOLFTPM2_KEYBLOB* rsaKey3;
|
WOLFTPM2_KEYBLOB* rsaKey3;
|
||||||
|
#else
|
||||||
|
WOLFTPM2_KEYBLOB key2[1];
|
||||||
|
WOLFTPM2_KEYBLOB rsaKey3[1];
|
||||||
|
#endif
|
||||||
TPM2B_DIGEST seedValue;
|
TPM2B_DIGEST seedValue;
|
||||||
TPMT_PUBLIC publicTemplate3;
|
TPMT_PUBLIC publicTemplate3;
|
||||||
TPMA_OBJECT attributes;
|
TPMA_OBJECT attributes;
|
||||||
|
@ -122,8 +127,10 @@ int TPM2_ExternalImport_Example(void* userCtx, int argc, char *argv[])
|
||||||
argc--;
|
argc--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef WOLFTPM2_NO_HEAP
|
||||||
key2 = wolfTPM2_NewKeyBlob();
|
key2 = wolfTPM2_NewKeyBlob();
|
||||||
rsaKey3 = wolfTPM2_NewKeyBlob();
|
rsaKey3 = wolfTPM2_NewKeyBlob();
|
||||||
|
#endif
|
||||||
primary = &storage;
|
primary = &storage;
|
||||||
|
|
||||||
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
||||||
|
@ -229,8 +236,10 @@ exit:
|
||||||
wolfTPM2_UnloadHandle(&dev, &key2->handle);
|
wolfTPM2_UnloadHandle(&dev, &key2->handle);
|
||||||
wolfTPM2_UnloadHandle(&dev, &primary->handle);
|
wolfTPM2_UnloadHandle(&dev, &primary->handle);
|
||||||
|
|
||||||
|
#ifndef WOLFTPM2_NO_HEAP
|
||||||
wolfTPM2_FreeKeyBlob(key2);
|
wolfTPM2_FreeKeyBlob(key2);
|
||||||
wolfTPM2_FreeKeyBlob(rsaKey3);
|
wolfTPM2_FreeKeyBlob(rsaKey3);
|
||||||
|
#endif
|
||||||
|
|
||||||
wolfTPM2_Cleanup(&dev);
|
wolfTPM2_Cleanup(&dev);
|
||||||
|
|
||||||
|
|
|
@ -89,8 +89,8 @@
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
typedef struct SockIoCbCtx {
|
typedef struct SockIoCbCtx {
|
||||||
int listenFd;
|
SOCKET_T listenFd;
|
||||||
int fd;
|
SOCKET_T fd;
|
||||||
} SockIoCbCtx;
|
} SockIoCbCtx;
|
||||||
|
|
||||||
#ifndef WOLFSSL_USER_IO
|
#ifndef WOLFSSL_USER_IO
|
||||||
|
@ -266,7 +266,7 @@ static inline int SetupSocketAndListen(SockIoCbCtx* sockIoCtx, word32 port)
|
||||||
|
|
||||||
static inline int SocketWaitClient(SockIoCbCtx* sockIoCtx)
|
static inline int SocketWaitClient(SockIoCbCtx* sockIoCtx)
|
||||||
{
|
{
|
||||||
int connd;
|
SOCKET_T connd;
|
||||||
struct sockaddr_in clientAddr;
|
struct sockaddr_in clientAddr;
|
||||||
XSOCKLENT size = sizeof(clientAddr);
|
XSOCKLENT size = sizeof(clientAddr);
|
||||||
|
|
||||||
|
|
|
@ -642,7 +642,7 @@ TPM_RC TPM2_Init_ex(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCtx,
|
||||||
/* Setup HAL IO Callback */
|
/* Setup HAL IO Callback */
|
||||||
rc = TPM2_SetHalIoCb(ctx, ioCb, userCtx);
|
rc = TPM2_SetHalIoCb(ctx, ioCb, userCtx);
|
||||||
if (rc != TPM_RC_SUCCESS)
|
if (rc != TPM_RC_SUCCESS)
|
||||||
return rc;
|
return rc;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Set the active TPM global */
|
/* Set the active TPM global */
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
/* For some struct to buffer conversions */
|
/* For some struct to buffer conversions */
|
||||||
#include <wolftpm/tpm2_packet.h>
|
#include <wolftpm/tpm2_packet.h>
|
||||||
|
#include <hal/tpm_io.h> /* for default IO callback */
|
||||||
|
|
||||||
/* Local Functions */
|
/* Local Functions */
|
||||||
static int wolfTPM2_GetCapabilities_NoDev(WOLFTPM2_CAPS* cap);
|
static int wolfTPM2_GetCapabilities_NoDev(WOLFTPM2_CAPS* cap);
|
||||||
|
@ -182,7 +182,7 @@ WOLFTPM2_DEV* wolfTPM2_New(void)
|
||||||
WOLFTPM2_DEV *dev = (WOLFTPM2_DEV*)XMALLOC(
|
WOLFTPM2_DEV *dev = (WOLFTPM2_DEV*)XMALLOC(
|
||||||
sizeof(WOLFTPM2_DEV), NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
sizeof(WOLFTPM2_DEV), NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||||
if (dev != NULL) {
|
if (dev != NULL) {
|
||||||
if (wolfTPM2_Init(dev, NULL, NULL) != TPM_RC_SUCCESS) {
|
if (wolfTPM2_Init(dev, TPM2_IoCb, NULL) != TPM_RC_SUCCESS) {
|
||||||
XFREE(dev, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
XFREE(dev, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||||
dev = NULL;
|
dev = NULL;
|
||||||
}
|
}
|
||||||
|
@ -797,20 +797,27 @@ static int wolfTPM2_GetCapabilities_NoDev(WOLFTPM2_CAPS* cap)
|
||||||
#if defined(WOLFTPM_SLB9672) || defined(WOLFTPM_SLB9673)
|
#if defined(WOLFTPM_SLB9672) || defined(WOLFTPM_SLB9673)
|
||||||
/* Get vendor specific information */
|
/* Get vendor specific information */
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
rc = tpm2_ifx_cap_vendor_get(cap, TPM_PT_VENDOR_FIX_FU_OPERATION_MODE,
|
int rc_ifx;
|
||||||
|
rc_ifx = tpm2_ifx_cap_vendor_get(cap, TPM_PT_VENDOR_FIX_FU_OPERATION_MODE,
|
||||||
&cap->opMode, sizeof(cap->opMode));
|
&cap->opMode, sizeof(cap->opMode));
|
||||||
}
|
if (rc_ifx == 0) {
|
||||||
if (rc == 0) {
|
rc_ifx = tpm2_ifx_cap_vendor_get(cap, TPM_PT_VENDOR_FIX_FU_KEYGROUP_ID,
|
||||||
rc = tpm2_ifx_cap_vendor_get(cap, TPM_PT_VENDOR_FIX_FU_KEYGROUP_ID,
|
(uint8_t*)&cap->keyGroupId, sizeof(cap->keyGroupId));
|
||||||
(uint8_t*)&cap->keyGroupId, sizeof(cap->keyGroupId));
|
}
|
||||||
}
|
if (rc_ifx == 0) {
|
||||||
if (rc == 0) {
|
rc_ifx = tpm2_ifx_cap_vendor_get(cap, TPM_PT_VENDOR_FIX_FU_COUNTER,
|
||||||
rc = tpm2_ifx_cap_vendor_get(cap, TPM_PT_VENDOR_FIX_FU_COUNTER,
|
(uint8_t*)&cap->fwCounter, sizeof(cap->fwCounter));
|
||||||
(uint8_t*)&cap->fwCounter, sizeof(cap->fwCounter));
|
}
|
||||||
}
|
if (rc_ifx == 0) {
|
||||||
if (rc == 0) {
|
rc_ifx = tpm2_ifx_cap_vendor_get(cap, TPM_PT_VENDOR_FIX_FU_COUNTER_SAME,
|
||||||
rc = tpm2_ifx_cap_vendor_get(cap, TPM_PT_VENDOR_FIX_FU_COUNTER_SAME,
|
(uint8_t*)&cap->fwCounterSame, sizeof(cap->fwCounterSame));
|
||||||
(uint8_t*)&cap->fwCounterSame, sizeof(cap->fwCounterSame));
|
}
|
||||||
|
if (rc_ifx != 0) {
|
||||||
|
#ifdef DEBUG_WOLFTPM
|
||||||
|
printf("Error getting Infineon vendor capabilities 0x%x: %s\n",
|
||||||
|
rc_ifx, TPM2_GetRCString(rc_ifx));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -1142,7 +1149,7 @@ int wolfTPM2_Cleanup_ex(WOLFTPM2_DEV* dev, int doShutdown)
|
||||||
return rc;
|
return rc;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (doShutdown) {
|
if (doShutdown && TPM2_GetActiveCtx() != NULL) {
|
||||||
Shutdown_In shutdownIn;
|
Shutdown_In shutdownIn;
|
||||||
XMEMSET(&shutdownIn, 0, sizeof(shutdownIn));
|
XMEMSET(&shutdownIn, 0, sizeof(shutdownIn));
|
||||||
shutdownIn.shutdownType = TPM_SU_CLEAR;
|
shutdownIn.shutdownType = TPM_SU_CLEAR;
|
||||||
|
|
|
@ -34,8 +34,8 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LIBWOLFTPM_VERSION_STRING "3.2.0"
|
#define LIBWOLFTPM_VERSION_STRING "3.4.0"
|
||||||
#define LIBWOLFTPM_VERSION_HEX 0x03002000
|
#define LIBWOLFTPM_VERSION_HEX 0x03004000
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue