Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions wolfcrypt/src/dh.c
Original file line number Diff line number Diff line change
Expand Up @@ -2446,6 +2446,26 @@ static int wc_DhAgree_Async(DhKey* key, byte* agree, word32* agreeSz,
pBits = mp_unsigned_bin_size(&key->p) * 8;
if (pBits == 768 || pBits == 1024 || pBits == 1536 ||
pBits == 2048 || pBits == 3072 || pBits == 4096) {
/* same checks as wc_DhAgree_Sync */
if (mp_count_bits(&key->p) < DH_MIN_SIZE) {
WOLFSSL_MSG("DH prime smaller than DH_MIN_SIZE");
return WC_KEY_SIZE_E;
}
if (mp_iseven(&key->p) == MP_YES) {
return MP_VAL;
}
#ifdef WOLFSSL_VALIDATE_FFC_IMPORT
if (wc_DhCheckPrivKey(key, priv, privSz) != 0) {
WOLFSSL_MSG("wc_DhAgree wc_DhCheckPrivKey failed");
return DH_CHECK_PRIV_E;
}
#endif
/* Always validate peer public key (2 <= y <= p-2) per SP 800-56A */
if (wc_DhCheckPubKey_ex(key, otherPub, pubSz, NULL, 0) != 0) {
WOLFSSL_MSG("wc_DhAgree wc_DhCheckPubKey failed");
return DH_CHECK_PUB_E;
}

ret = wc_mp_to_bigint(&key->p, &key->p.raw);
if (ret == MP_OKAY)
ret = IntelQaDhAgree(&key->asyncDev, &key->p.raw,
Expand Down
48 changes: 48 additions & 0 deletions wolfcrypt/src/port/intel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,53 @@ cd ../wolfssl
make
```

An Intel QAT 8970 adapter appears as three C62x devices, as in the output above.

### After a kernel upgrade

The QAT driver is built out of tree, so it has to be rebuilt and reinstalled for each new kernel. Until then `systemctl status qat` still reports `active`, but its log shows `modprobe: FATAL: Module usdm_drv not found`, `adf_ctl status` cannot open `/dev/qat_adf_ctl`, and wolfCrypt prints `Running without async`.

### Building QAT 4.28 on Linux 7.0

Linux 7.0 removed two things the QAT 4.28 driver uses. Linux 6.14 still has both.

* `iommu_domain_alloc()` is gone. In `quickassist/qat/drivers/crypto/qat/qat_common/qdm.c`, `qdm_init()` allocates the domain for an IOMMU-mapped PCI device instead:

```c
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 18, 0)
{
struct pci_dev *pdev = NULL;

domain = NULL;
while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev))) {
if (device_iommu_mapped(&pdev->dev)) {
domain = iommu_paging_domain_alloc(&pdev->dev);
pci_dev_put(pdev);
if (IS_ERR(domain))
domain = NULL;
break;
}
}
}
#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)
```

* Kbuild no longer reads `EXTRA_CFLAGS`, so the utility modules cannot find their headers. Pass the flags as `ccflags-y`. The driver itself sets its own `ccflags-y`, so build it first without the override:

```sh
./configure --enable-legacy-algorithms
(cd quickassist/qat && make)
make quickassist-all 'ccflags-y=$(EXTRA_CFLAGS)' KBUILD_MODPOST_WARN=1
make adf-ctl-all
sudo make qat-driver-install quickassist-install adf-ctl-install \
qat-conf-install qat-fw-install qat-service-files-install \
qat-service-install
```

`qat_api.ko`, the in-kernel crypto API module, reports an unresolved `__SCK__WARN_trap` symbol. The QAT service does not load it and user-space crypto does not need it.

`qat-conf-install` moves the existing `/etc/<device>_dev*.conf` files to `/etc/qat_conf_backup/` before copying new ones from `build/`, which this build does not populate. The devices then stay `down` and the service log shows `No GENERAL section found`. Back up the configuration files first and copy them back, or copy the defaults from `quickassist/utilities/adf_ctl/conf_files/`, then `sudo systemctl restart qat`.


## Usage

Expand Down Expand Up @@ -126,6 +173,7 @@ ls /sys/kernel/debug/qat_c6xx_<bdf>/ # heartbeat, fw_co
Common causes seen in practice:

- Hugepage mismatch: `usdm_drv` loaded with `max_huge_pages` > 0 but `HugePages_Total` is smaller (or 0) -- every QAT process then fails its SAL memory init. Either reserve enough hugepages at boot (GRUB `default_hugepagesz=2M hugepagesz=2M hugepages=N`) or reload `usdm_drv` with `max_huge_pages=0` (non-hugepage contiguous memory). Runtime `sysctl vm.nr_hugepages=N` is unreliable once memory is fragmented.
- Fragmented memory without hugepages: processes fail with `ioctl_alloc_slab ... ioctl call for mem allocation failed` and `Lac_MemPoolCreate() - : Unable to allocate contiguous chunk of memory`, although plenty of memory is free. The last columns of `/proc/buddyinfo` (the largest free blocks) are 0 or close to it. Dropping caches and compacting memory restores them without a reboot: `sudo bash -c 'sync; echo 3 > /proc/sys/vm/drop_caches; echo 1 > /proc/sys/vm/compact_memory'`.
- Orphan rings: `dmesg` shows `Process <pid> ... exit with orphan rings`. A QAT process exited (often killed by a test timeout) without an orderly `wolfAsync_HardwareStop`/`icp_sal_userStop`, leaking instance rings. These accumulate (especially under `make -j` test runs) until the device's instance pool is exhausted and new processes can no longer init. Recover with a full driver-stack reload (`systemctl stop qat; rmmod usdm_drv qat_c62x intel_qat; modprobe qat_c62x; modprobe usdm_drv; systemctl start qat`) or a reboot. Always shut down QAT-using processes cleanly; avoid `make -j` (see make check above).
- PCIe Advanced Error Reporting errors (`UESta`/`CESta` flags set) or `dmesg` heartbeat/RAS/reset messages indicate a real firmware hang or bus error; with `AutoResetOnError = 0` (default in the device conf) the device stays down until a manual `adf_ctl reset`. Set `AutoResetOnError = 1` in `/etc/<device>_dev*.conf` for self-healing.

Expand Down
45 changes: 26 additions & 19 deletions wolfcrypt/src/port/intel/quickassist.c
Original file line number Diff line number Diff line change
Expand Up @@ -1158,13 +1158,14 @@ int IntelQaGenPrime(WC_ASYNC_DEV* dev, WC_RNG* rng, byte* primeBuf,
/* if rollover occurred increment high order bytes */
/* increment by 1 does not affect odd/even */
int j;
primeData[byteCheck] = 0x01;
for (j = primeSz - 2; j >= 0; j--) {
if (primeData[i] != 0xFF) {
primeData[i] += 1;
if (primeData[j] != 0xFF) {
primeData[j] += 1;
break;
}
else {
primeData[i] = 0;
primeData[j] = 0;
}
}
}
Expand Down Expand Up @@ -4443,23 +4444,31 @@ static void IntelQaDhAgreeCallback(void *pCallbackTag, CpaStatus status,
}

/* count leading zeros */
while (out[idxTrim] == 0 && idxTrim < pOut->dataLenInBytes) {
while (idxTrim < pOut->dataLenInBytes && out[idxTrim] == 0) {
idxTrim++;
}
pOut->dataLenInBytes -= idxTrim;

/* return data and trim leading zeros */
if (dev->qat.out && (dev->qat.out != pOut->pData || idxTrim > 0)) {
XMEMMOVE(dev->qat.out, &out[idxTrim], pOut->dataLenInBytes);
/* The shared secret must not be 0 or 1 (SP 800-56A 5.7.1.1). */
if (pOut->dataLenInBytes == 0 ||
(pOut->dataLenInBytes == 1 && out[idxTrim] == 1)) {
WOLFSSL_MSG("IntelQaDhAgreeCallback: invalid shared secret");
ret = MP_VAL;
}
else {
/* return data and trim leading zeros */
if (dev->qat.out && (dev->qat.out != pOut->pData || idxTrim > 0)) {
XMEMMOVE(dev->qat.out, &out[idxTrim], pOut->dataLenInBytes);
}

/* return final length */
if (dev->qat.outLenPtr) {
*dev->qat.outLenPtr = pOut->dataLenInBytes;
}
/* return final length */
if (dev->qat.outLenPtr) {
*dev->qat.outLenPtr = pOut->dataLenInBytes;
}

/* mark event result */
ret = 0; /* success */
/* mark event result */
ret = 0; /* success */
}
}
(void)opData;

Expand Down Expand Up @@ -4777,21 +4786,19 @@ int IntelQaDrbg(WC_ASYNC_DEV* dev, byte* rngBuf, word32 rngSz)
/* chunk into LAC_DRBG_MAX_NUM_OF_BYTES (0xFFFF) */
while (ret == 0 && idx < rngSz) {
/* setup operation data */
gen = rngSz - gen;
gen = rngSz - idx;
if (gen > 0xFFFF)
gen = 0xFFFF;

/* the buffer is freed when the previous chunk completes */
pOut->dataLenInBytes = gen;
if (idx == 0 && pOut->pData == NULL) {
pOut->pData = XREALLOC(rngBuf, gen, dev->heap,
if (pOut->pData == NULL) {
pOut->pData = (Cpa8U*)XMALLOC(gen, dev->heap,
DYNAMIC_TYPE_ASYNC_NUMA);
if (pOut->pData == NULL) {
ret = MEMORY_E; goto exit;
}
}
else {
XMEMCPY(pOut->pData, &rngBuf[idx], gen);
}

opData->sessionHandle = dev->qat.op.drbg.handle;
opData->lengthInBytes = gen;
Expand Down
14 changes: 8 additions & 6 deletions wolfcrypt/src/port/intel/quickassist_mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,9 @@ void* IntelQaRealloc(void *ptr, size_t size, void* heap, int type
byte allocNew = 1;
int newIsNuma = -1, ptrIsNuma = -1;
size_t copySize = 0;
/* The port asks for a DMA copy of a buffer the caller keeps */
int isCopy = (type == DYNAMIC_TYPE_ASYNC_NUMA ||
type == DYNAMIC_TYPE_ASYNC_NUMA64);

#ifndef USE_QAE_THREAD_LS
int ret = pthread_mutex_lock(&g_memLock);
Expand Down Expand Up @@ -593,8 +596,9 @@ void* IntelQaRealloc(void *ptr, size_t size, void* heap, int type
else
#endif
{
/* use existing pointer and increment counter */
header->count++;
/* use existing pointer, with a reference for a copy */
if (isCopy)
header->count++;
newPtr = origPtr;
allocNew = 0;
}
Expand All @@ -619,10 +623,8 @@ void* IntelQaRealloc(void *ptr, size_t size, void* heap, int type
copySize = size;
XMEMCPY(newPtr, ptr, copySize);

if (ptrIsNuma == 0) {
/* old pointer is a qae-headed non-NUMA buffer (software
* fallback) -- free it. Caller-owned (ptrIsNuma == -1) and
* NUMA (== 1) buffers are handled by their own paths. */
if (!isCopy && ptrIsNuma >= 0) {
/* normal REALLOC: free old pointer */
_qaeMemFree(ptr, heap, type
#ifdef WOLFSSL_DEBUG_MEMORY
, func, line
Expand Down
88 changes: 88 additions & 0 deletions wolfcrypt/test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -38777,6 +38777,54 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t memory_test(void)
}
#endif

#if defined(WOLFSSL_ASYNC_CRYPT) && defined(HAVE_INTEL_QA)
/* A DMA copy made with XREALLOC leaves the caller's buffer allocated. */
{
byte *b = (byte*)XMALLOC(12, HEAP_HINT,
DYNAMIC_TYPE_CIPHER);
byte *c = NULL;
if (b == NULL)
return WC_TEST_RET_ENC_ERRNO;
XMEMSET(b, 0xA5, 12);
c = (byte*)XREALLOC(b, 16, HEAP_HINT,
DYNAMIC_TYPE_ASYNC_NUMA);
if (c == NULL) {
XFREE(b, HEAP_HINT, DYNAMIC_TYPE_CIPHER);
return WC_TEST_RET_ENC_ERRNO;
}
if (c == b || XMEMCMP(c, b, 12) != 0)
ret = WC_TEST_RET_ENC_NC;
XMEMSET(b, 0x5A, 12);
if (c != b)
XFREE(c, HEAP_HINT, DYNAMIC_TYPE_ASYNC_NUMA);
XFREE(b, HEAP_HINT, DYNAMIC_TYPE_CIPHER);
if (ret != 0)
return ret;
}
/* Any other XREALLOC is a normal realloc, even for a type the device can
* use, and the old buffer is released. */
{
byte *b = (byte*)XMALLOC(16, HEAP_HINT, DYNAMIC_TYPE_PUBLIC_KEY);
byte *c = NULL;
int k;
if (b == NULL)
return WC_TEST_RET_ENC_ERRNO;
XMEMSET(b, 0xA5, 16);
c = (byte*)XREALLOC(b, 64, HEAP_HINT, DYNAMIC_TYPE_PUBLIC_KEY);
if (c == NULL) {
XFREE(b, HEAP_HINT, DYNAMIC_TYPE_PUBLIC_KEY);
return WC_TEST_RET_ENC_ERRNO;
}
for (k = 0; k < 16; k++) {
if (c[k] != 0xA5)
ret = WC_TEST_RET_ENC_NC;
}
XFREE(c, HEAP_HINT, DYNAMIC_TYPE_PUBLIC_KEY);
if (ret != 0)
return ret;
}
#endif

if (ret == 0) {
/* This test is only interesting on embedded R/O Flash systems */
if (const_byte_ptr_test(const_byte_array, &j) != CBPTR_EXPECTED) {
Expand Down Expand Up @@ -45420,6 +45468,39 @@ static wc_test_ret_t dh_agree_ct_test(DhKey* key, DhKey* key2,
exit_dh_agree_ct:
return ret;
}
#ifndef WOLFSSL_KCAPI_DH
/* wc_DhAgree rejects an invalid peer public value, whichever backend it uses. */
static wc_test_ret_t dh_agree_bad_pub_test(DhKey* key, const byte* priv,
word32 privSz, word32 pubSz, byte* y, byte* agree, word32 bufSize)
{
wc_test_ret_t ret;
word32 agreeSz;
word32 ySz;
int i;

for (i = 0; i < 3; i++) {
/* 0, 1, and a value larger than the prime. A generated public key
* can be shorter than the prime, so fill the whole buffer, which
* holds at least the prime, for that one. */
ySz = (i == 2) ? bufSize : pubSz;
XMEMSET(y, (i == 2) ? 0xFF : 0x00, ySz);
if (i == 1)
y[ySz - 1] = 1;

agreeSz = bufSize;
PRIVATE_KEY_UNLOCK();
ret = wc_DhAgree(key, agree, &agreeSz, priv, privSz, y, ySz);
PRIVATE_KEY_LOCK();
#if defined(WOLFSSL_ASYNC_CRYPT)
ret = wc_AsyncWait(ret, &key->asyncDev, WC_ASYNC_FLAG_NONE);
#endif
if (ret != WC_NO_ERR_TRACE(DH_CHECK_PUB_E))
return WC_TEST_RET_ENC_I(i);
}

return 0;
}
#endif
#endif /* !WC_NO_RNG && (!HAVE_FIPS || FIPS_VERSION_GE(7,0)) && !HAVE_SELFTEST */

#if defined(WOLFSSL_DH_EXTRA) && !defined(NO_FILESYSTEM) && \
Expand Down Expand Up @@ -45746,6 +45827,13 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t dh_test(void)
priv2, privSz2, pub2, pubSz2, agree, agree2, DH_TEST_BUF_SIZE);
if (ret != 0)
ERROR_OUT(ret, done);

#ifndef WOLFSSL_KCAPI_DH
ret = dh_agree_bad_pub_test(key, priv, privSz, pubSz, agree2, agree,
DH_TEST_BUF_SIZE);
if (ret != 0)
ERROR_OUT(ret, done);
#endif
#endif /* !WC_NO_RNG && (!HAVE_FIPS || FIPS_VERSION_GE(7,0)) && !HAVE_SELFTEST */

/* Test DH key import / export */
Expand Down
Loading