Blame SOURCES/0025-COMMON-EP11-Add-Kyber-key-type-and-mechanism.patch

397a93
From 49ce634ed6fa1fddc2bb34fd0f89c0ea0cc368ee Mon Sep 17 00:00:00 2001
397a93
From: Ingo Franzki <ifranzki@linux.ibm.com>
397a93
Date: Tue, 1 Mar 2022 09:15:15 +0100
397a93
Subject: [PATCH 25/34] COMMON/EP11: Add Kyber key type and mechanism
397a93
397a93
Kyber requires an EP11 host library of version 4.0 or later,
397a93
and a CEX8P crypto card.
397a93
397a93
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
397a93
---
397a93
 usr/lib/api/mechtable.inc            |   1 +
397a93
 usr/lib/api/policy.c                 |   2 +
397a93
 usr/lib/common/asn1.c                | 563 +++++++++++++++++++++++++++++++++++
397a93
 usr/lib/common/h_extern.h            |  59 ++++
397a93
 usr/lib/common/key.c                 | 476 +++++++++++++++++++++++++++++
397a93
 usr/lib/common/key_mgr.c             |  10 +
397a93
 usr/lib/common/mech_ec.c             |   3 +
397a93
 usr/lib/common/template.c            |  12 +
397a93
 usr/lib/ep11_stdll/ep11_specific.c   |  42 ++-
397a93
 usr/lib/ep11_stdll/ep11cpfilter.conf |   2 +-
397a93
 10 files changed, 1167 insertions(+), 3 deletions(-)
397a93
397a93
diff --git a/usr/lib/api/mechtable.inc b/usr/lib/api/mechtable.inc
397a93
index e3d14e3e..7aa72fbf 100644
397a93
--- a/usr/lib/api/mechtable.inc
397a93
+++ b/usr/lib/api/mechtable.inc
397a93
@@ -84,6 +84,7 @@ const struct mechrow mechtable_rows[] =
397a93
      { "CKM_IBM_EC_X448",             CKM_IBM_EC_X448,               0, MC_INFORMATION_UNAVAILABLE, MCF_DERIVE },
397a93
      { "CKM_IBM_ED25519_SHA512",      CKM_IBM_ED25519_SHA512,      128,           MC_KEY_DEPENDENT, MCF_SIGNVERIFY },
397a93
      { "CKM_IBM_ED448_SHA3",          CKM_IBM_ED448_SHA3,          144,           MC_KEY_DEPENDENT, MCF_SIGNVERIFY },
397a93
+     { "CKM_IBM_KYBER",               CKM_IBM_KYBER,                 0,           MC_KEY_DEPENDENT, MCF_KEYGEN | MCF_ENCRYPTDECRYPT | MCF_DERIVE | MCF_NEEDSPARAM},
397a93
      { "CKM_IBM_SHA3_224",            CKM_IBM_SHA3_224,            144,                         24, MCF_DIGEST },
397a93
      { "CKM_IBM_SHA3_224_HMAC",       CKM_IBM_SHA3_224_HMAC,       144,                         24, MCF_SIGNVERIFY },
397a93
      { "CKM_IBM_SHA3_256",            CKM_IBM_SHA3_256,            136,                         32, MCF_DIGEST },
397a93
diff --git a/usr/lib/api/policy.c b/usr/lib/api/policy.c
397a93
index 4bee5180..b513a8a9 100644
397a93
--- a/usr/lib/api/policy.c
397a93
+++ b/usr/lib/api/policy.c
397a93
@@ -333,6 +333,7 @@ static CK_RV policy_extract_key_data(get_attr_val_f getattr, void *d,
397a93
         *comptarget = COMPARE_SYMMETRIC;
397a93
         break;
397a93
     case CKK_IBM_PQC_DILITHIUM:
397a93
+    case CKK_IBM_PQC_KYBER:
397a93
         rv = policy_get_pqc_args(*(CK_ULONG *)keytype->pValue, getattr, d,
397a93
                                  free_attr, size, siglen, oid, oidlen);
397a93
         *comptarget = COMPARE_PQC;
397a93
@@ -1062,6 +1063,7 @@ static CK_RV policy_update_mech_info(policy_t p, CK_MECHANISM_TYPE mech,
397a93
             }
397a93
             break;
397a93
         case CKM_IBM_DILITHIUM:
397a93
+        case CKM_IBM_KYBER:
397a93
             break;
397a93
         case CKM_IBM_SHA3_224:
397a93
         case CKM_IBM_SHA3_256:
397a93
diff --git a/usr/lib/common/asn1.c b/usr/lib/common/asn1.c
397a93
index 85d3924c..87cc5dfc 100644
397a93
--- a/usr/lib/common/asn1.c
397a93
+++ b/usr/lib/common/asn1.c
397a93
@@ -4384,3 +4384,566 @@ cleanup:
397a93
 
397a93
     return rc;
397a93
 }
397a93
+
397a93
+/**
397a93
+ * An IBM Kyber public key is given by:
397a93
+ *
397a93
+ *  SEQUENCE (2 elem)
397a93
+ *    SEQUENCE (2 elem)
397a93
+ *      OBJECT IDENTIFIER 1.3.6.1.4.1.2.267.5.xxx
397a93
+ *      NULL
397a93
+ *    BIT STRING (1 elem)
397a93
+ *      SEQUENCE (1 elem)
397a93
+ *        pk BIT STRING -- public key
397a93
+ */
397a93
+CK_RV ber_encode_IBM_KyberPublicKey(CK_BBOOL length_only,
397a93
+                                    CK_BYTE **data, CK_ULONG *data_len,
397a93
+                                    const CK_BYTE *oid, CK_ULONG oid_len,
397a93
+                                    CK_ATTRIBUTE *pk)
397a93
+{
397a93
+    CK_BYTE *buf = NULL, *buf2 = NULL, *buf3 = NULL, *buf4 = NULL;
397a93
+    CK_BYTE *buf5 = NULL, *algid = NULL;
397a93
+    CK_ULONG len, len4, offset, total, total_len, algid_len;
397a93
+    CK_RV rc;
397a93
+
397a93
+    UNUSED(length_only);
397a93
+
397a93
+    offset = 0;
397a93
+    rc = 0;
397a93
+    total_len = 0;
397a93
+    total = 0;
397a93
+
397a93
+    /* Calculate storage for AlgID sequence */
397a93
+    rc |= ber_encode_SEQUENCE(TRUE, NULL, &total_len, NULL,
397a93
+                              oid_len + ber_NULLLen);
397a93
+
397a93
+    /* Calculate storage for inner sequence */
397a93
+    rc |= ber_encode_INTEGER(TRUE, NULL, &len, NULL, pk->ulValueLen);
397a93
+    offset += len;
397a93
+
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("%s ber_encode_Int failed with rc=0x%lx\n", __func__, rc);
397a93
+        return rc;
397a93
+    }
397a93
+
397a93
+    /* Allocate storage for inner sequence */
397a93
+    buf = (CK_BYTE *) malloc(offset);
397a93
+    if (!buf) {
397a93
+        TRACE_ERROR("%s Memory allocation failed\n", __func__);
397a93
+        return CKR_HOST_MEMORY;
397a93
+    }
397a93
+
397a93
+    /**
397a93
+     *    SEQUENCE (1 elem)
397a93
+     *       BIT STRING -> pk
397a93
+     */
397a93
+    offset = 0;
397a93
+    rc = ber_encode_BIT_STRING(FALSE, &buf2, &len,
397a93
+                               pk->pValue, pk->ulValueLen, 0);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("%s ber_encode_Int failed with rc=0x%lx\n", __func__, rc);
397a93
+        goto error;
397a93
+    }
397a93
+    memcpy(buf + offset, buf2, len);
397a93
+    offset += len;
397a93
+    free(buf2);
397a93
+
397a93
+    rc = ber_encode_SEQUENCE(FALSE, &buf2, &len, buf, offset);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("%s ber_encode_Seq failed with rc=0x%lx\n", __func__, rc);
397a93
+        goto error;
397a93
+    }
397a93
+    free(buf);
397a93
+    buf = NULL;
397a93
+
397a93
+    /* Calculate length of outer sequence */
397a93
+    rc = ber_encode_BIT_STRING(TRUE, NULL, &total, buf2, len, 0);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("%s ber_encode_Oct_Str failed with rc=0x%lx\n", __func__, rc);
397a93
+        goto error;
397a93
+    } else {
397a93
+        total_len += total;
397a93
+    }
397a93
+
397a93
+    /* Allocate storage for outer sequence and bit string */
397a93
+    buf3 = (CK_BYTE *) malloc(total_len);
397a93
+    if (!buf3) {
397a93
+        TRACE_ERROR("%s Memory allocation failed\n", __func__);
397a93
+        rc = CKR_HOST_MEMORY;
397a93
+        goto error;
397a93
+    }
397a93
+
397a93
+    /*
397a93
+     * SEQUENCE (2 elem)
397a93
+     *      OBJECT IDENTIFIER 1.3.6.1.4.1.2.267.5.xxx
397a93
+     *      NULL  <- no parms for this oid
397a93
+     */
397a93
+    buf5 = (CK_BYTE *) malloc(oid_len + ber_NULLLen);
397a93
+    if (!buf5) {
397a93
+        TRACE_ERROR("%s Memory allocation failed\n", __func__);
397a93
+        rc = CKR_HOST_MEMORY;
397a93
+        goto error;
397a93
+    }
397a93
+    memcpy(buf5, oid, oid_len);
397a93
+    memcpy(buf5 + oid_len, ber_NULL, ber_NULLLen);
397a93
+
397a93
+    rc = ber_encode_SEQUENCE(FALSE, &algid, &algid_len, buf5,
397a93
+                             oid_len + ber_NULLLen);
397a93
+    free(buf5);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("%s ber_encode_SEQUENCE failed with rc=0x%lx\n", __func__, rc);
397a93
+        goto error;
397a93
+    }
397a93
+
397a93
+    total_len = algid_len;
397a93
+    memcpy(buf3, algid, algid_len);
397a93
+    free(algid);
397a93
+    algid = NULL;
397a93
+
397a93
+    /*
397a93
+     * BIT STRING (1 elem)
397a93
+     *       SEQUENCE (1 elem)
397a93
+     *          BIT STRING  -> pk
397a93
+     */
397a93
+    rc = ber_encode_BIT_STRING(FALSE, &buf4, &len4, buf2, len, 0);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("%s ber_encode_BIT_STRING failed with rc=0x%lx\n", __func__, rc);
397a93
+        goto error;
397a93
+    }
397a93
+    memcpy(buf3 + total_len, buf4, len4);
397a93
+    total_len += len4;
397a93
+    free(buf4);
397a93
+    buf4 = NULL;
397a93
+
397a93
+    /**
397a93
+     * SEQUENCE (2 elem)
397a93
+     *    SEQUENCE (2 elem)
397a93
+     *       OBJECT IDENTIFIER 1.3.6.1.4.1.2.267.5.xxx
397a93
+     *       NULL -> no parms for this oid
397a93
+     *    BIT STRING (1 elem)
397a93
+     *       SEQUENCE (2 elem)
397a93
+     *          BIT STRING  -> pk
397a93
+     */
397a93
+    rc = ber_encode_SEQUENCE(FALSE, data, data_len, buf3, total_len);
397a93
+    if (rc != CKR_OK)
397a93
+        TRACE_ERROR("%s ber_encode_Seq failed with rc=0x%lx\n", __func__, rc);
397a93
+
397a93
+error:
397a93
+
397a93
+    if (buf)
397a93
+        free(buf);
397a93
+    if (buf2)
397a93
+        free(buf2);
397a93
+    if (buf3)
397a93
+        free(buf3);
397a93
+
397a93
+    return rc;
397a93
+}
397a93
+
397a93
+CK_RV ber_decode_IBM_KyberPublicKey(CK_BYTE *data,
397a93
+                                    CK_ULONG data_len,
397a93
+                                    CK_ATTRIBUTE **pk_attr,
397a93
+                                    CK_ATTRIBUTE **value_attr,
397a93
+                                    const struct pqc_oid **oid)
397a93
+{
397a93
+    CK_ATTRIBUTE *pk_attr_temp = NULL;
397a93
+    CK_ATTRIBUTE *value_attr_temp = NULL;
397a93
+
397a93
+    CK_BYTE *algoid = NULL;
397a93
+    CK_ULONG algoid_len;
397a93
+    CK_BYTE *param = NULL;
397a93
+    CK_ULONG param_len;
397a93
+    CK_BYTE *val = NULL;
397a93
+    CK_ULONG val_len;
397a93
+    CK_BYTE *seq;
397a93
+    CK_ULONG seq_len;
397a93
+    CK_BYTE *pk;
397a93
+    CK_ULONG pk_len;
397a93
+    CK_ULONG field_len, raw_spki_len;
397a93
+    CK_RV rc;
397a93
+
397a93
+    UNUSED(data_len); // XXX can this parameter be removed ?
397a93
+
397a93
+    rc = ber_decode_SPKI(data, &algoid, &algoid_len, &param, &param_len,
397a93
+                         &val, &val_len);
397a93
+    if (rc != CKR_OK) {
397a93
+       TRACE_DEVEL("ber_decode_SPKI failed\n");
397a93
+       return rc;
397a93
+    }
397a93
+
397a93
+    *oid = find_pqc_by_oid(kyber_oids, algoid, algoid_len);
397a93
+    if (*oid == NULL) {
397a93
+        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
397a93
+        return CKR_FUNCTION_FAILED;
397a93
+    }
397a93
+
397a93
+    /* Decode sequence:
397a93
+     *     SEQUENCE (1 elem)
397a93
+     *       BIT STRING = pk
397a93
+     */
397a93
+    rc = ber_decode_SEQUENCE(val, &seq, &seq_len, &field_len);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_DEVEL("ber_decode_SEQUENCE failed\n");
397a93
+        return rc;
397a93
+    }
397a93
+
397a93
+    /* Decode pk */
397a93
+    rc = ber_decode_BIT_STRING(seq, &pk, &pk_len, &field_len);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_DEVEL("ber_decode_INTEGER failed\n");
397a93
+        return rc;
397a93
+    }
397a93
+    pk++; /* Remove unused-bits byte */
397a93
+    pk_len--;
397a93
+
397a93
+    /* Build pk attribute */
397a93
+    rc = build_attribute(CKA_IBM_KYBER_PK, pk, pk_len, &pk_attr_temp);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_DEVEL("build_attribute failed\n");
397a93
+        goto cleanup;
397a93
+    }
397a93
+
397a93
+    /* Add raw SPKI as CKA_VALUE to public key (z/OS ICSF compatibility) */
397a93
+    rc = ber_decode_SEQUENCE(data, &val, &val_len, &raw_spki_len);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("%s ber_decode_SEQUENCE failed with rc=0x%lx\n", __func__, rc);
397a93
+        goto cleanup;
397a93
+    }
397a93
+    rc = build_attribute(CKA_VALUE, data, raw_spki_len, &value_attr_temp);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_DEVEL("build_attribute failed\n");
397a93
+        goto cleanup;
397a93
+    }
397a93
+
397a93
+    *pk_attr = pk_attr_temp;
397a93
+    *value_attr = value_attr_temp;
397a93
+
397a93
+    return CKR_OK;
397a93
+
397a93
+cleanup:
397a93
+    if (pk_attr_temp)
397a93
+        free(pk_attr_temp);
397a93
+    if (value_attr_temp)
397a93
+        free(value_attr_temp);
397a93
+
397a93
+    return rc;
397a93
+}
397a93
+
397a93
+/**
397a93
+ * An IBM Kyber private key is given by:
397a93
+ *
397a93
+ *     KyberPrivateKey ::= SEQUENCE {
397a93
+ *       version INTEGER,     -- v0, reserved 0
397a93
+ *       sk BIT STRING,       -- private key
397a93
+ *       pk [0] IMPLICIT OPTIONAL {
397a93
+ *         pk||rs BIT STRING  -- public key (pk) concatenated with 2x32 bytes rs
397a93
+ *       }
397a93
+ *     }
397a93
+ */
397a93
+CK_RV ber_encode_IBM_KyberPrivateKey(CK_BBOOL length_only,
397a93
+                                     CK_BYTE **data,
397a93
+                                     CK_ULONG *data_len,
397a93
+                                     const CK_BYTE *oid, CK_ULONG oid_len,
397a93
+                                     CK_ATTRIBUTE *sk,
397a93
+                                     CK_ATTRIBUTE *pk)
397a93
+{
397a93
+    CK_BYTE *buf = NULL, *buf2 = NULL, *buf3 = NULL;
397a93
+    CK_BYTE *algid = NULL, *algid_buf = NULL, *pk_rs = NULL;
397a93
+    CK_ULONG len, len2 = 0, offset, algid_len = 0;
397a93
+    CK_BYTE version[] = { 0 };
397a93
+    CK_RV rc;
397a93
+
397a93
+    /* Calculate storage for sequence */
397a93
+    offset = 0;
397a93
+    rc = 0;
397a93
+
397a93
+    rc |= ber_encode_SEQUENCE(TRUE, NULL, &algid_len, NULL,
397a93
+                              oid_len + ber_NULLLen);
397a93
+
397a93
+    rc |= ber_encode_INTEGER(TRUE, NULL, &len, NULL, sizeof(version));
397a93
+    offset += len;
397a93
+    rc |= ber_encode_BIT_STRING(TRUE, NULL, &len, NULL, sk->ulValueLen, 0);
397a93
+    offset += len;
397a93
+    if (pk) {
397a93
+        rc |= ber_encode_BIT_STRING(TRUE, NULL, &len2, NULL,
397a93
+                                    pk->ulValueLen + 64, 0);
397a93
+        rc |= ber_encode_CHOICE(TRUE, 0, NULL, &len, NULL, len2);
397a93
+        offset += len;
397a93
+    }
397a93
+
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_DEVEL("Calculate storage for sequence failed\n");
397a93
+        return CKR_FUNCTION_FAILED;
397a93
+    }
397a93
+
397a93
+    if (length_only == TRUE) {
397a93
+        rc = ber_encode_SEQUENCE(TRUE, NULL, &len, NULL, offset);
397a93
+        if (rc != CKR_OK) {
397a93
+            TRACE_DEVEL("ber_encode_SEQUENCE failed\n");
397a93
+            return rc;
397a93
+        }
397a93
+        rc = ber_encode_PrivateKeyInfo(TRUE,
397a93
+                                       NULL, data_len,
397a93
+                                       NULL, algid_len,
397a93
+                                       NULL, len);
397a93
+        if (rc != CKR_OK) {
397a93
+            TRACE_DEVEL("ber_encode_PrivateKeyInfo failed\n");
397a93
+            return rc;
397a93
+        }
397a93
+        return rc;
397a93
+    }
397a93
+
397a93
+    /* Allocate storage for sequence */
397a93
+    buf = (CK_BYTE *) malloc(offset);
397a93
+    if (!buf) {
397a93
+        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
397a93
+        return CKR_HOST_MEMORY;
397a93
+    }
397a93
+    offset = 0;
397a93
+
397a93
+    /* Version */
397a93
+    rc = ber_encode_INTEGER(FALSE, &buf2, &len, version, sizeof(version));
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("ber_encode_INTEGER of version failed\n");
397a93
+        goto error;
397a93
+    }
397a93
+    memcpy(buf + offset, buf2, len);
397a93
+    offset += len;
397a93
+    free(buf2);
397a93
+    buf2 = NULL;
397a93
+
397a93
+    /* sk */
397a93
+    rc = ber_encode_BIT_STRING(FALSE, &buf2, &len,
397a93
+                            sk->pValue, sk->ulValueLen, 0);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("ber_encode_BIT_STRING of sk failed\n");
397a93
+        goto error;
397a93
+    }
397a93
+    memcpy(buf + offset, buf2, len);
397a93
+    offset += len;
397a93
+    free(buf2);
397a93
+    buf2 = NULL;
397a93
+
397a93
+    /* (pk) Optional bit-string of public key */
397a93
+    if (pk && pk->pValue) {
397a93
+        /* append rs to public key */
397a93
+        pk_rs = (CK_BYTE *)malloc(pk->ulValueLen + 64);
397a93
+        if (!pk_rs) {
397a93
+            TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
397a93
+            rc = CKR_HOST_MEMORY;
397a93
+            goto error;
397a93
+        }
397a93
+
397a93
+        memcpy(pk_rs, pk->pValue, pk->ulValueLen);
397a93
+        memset(pk_rs + pk->ulValueLen, 0x30, 64);
397a93
+
397a93
+        rc = ber_encode_BIT_STRING(FALSE, &buf3, &len2,
397a93
+                                   pk_rs, pk->ulValueLen + 64, 0);
397a93
+        rc |= ber_encode_CHOICE(FALSE, 0, &buf2, &len, buf3, len2);
397a93
+        if (rc != CKR_OK) {
397a93
+            TRACE_ERROR("encoding of pk value failed\n");
397a93
+            goto error;
397a93
+        }
397a93
+        memcpy(buf + offset, buf2, len);
397a93
+        offset += len;
397a93
+        free(buf2);
397a93
+        buf2 = NULL;
397a93
+    }
397a93
+
397a93
+    /* Encode sequence */
397a93
+    rc = ber_encode_SEQUENCE(FALSE, &buf2, &len, buf, offset);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("ber_encode_SEQUENCE failed\n");
397a93
+        goto error;
397a93
+    }
397a93
+
397a93
+    algid_buf = (CK_BYTE *) malloc(oid_len + ber_NULLLen);
397a93
+    if (!algid_buf) {
397a93
+        TRACE_ERROR("%s Memory allocation failed\n", __func__);
397a93
+        rc = CKR_HOST_MEMORY;
397a93
+        goto error;
397a93
+    }
397a93
+    memcpy(algid_buf, oid, oid_len);
397a93
+    memcpy(algid_buf + oid_len, ber_NULL, ber_NULLLen);
397a93
+
397a93
+    rc = ber_encode_SEQUENCE(FALSE, &algid, &algid_len, algid_buf,
397a93
+                             oid_len + ber_NULLLen);
397a93
+    free(algid_buf);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("%s ber_encode_SEQUENCE failed with rc=0x%lx\n", __func__, rc);
397a93
+        goto error;
397a93
+    }
397a93
+
397a93
+    rc = ber_encode_PrivateKeyInfo(FALSE,
397a93
+                                   data, data_len,
397a93
+                                   algid, algid_len,
397a93
+                                   buf2, len);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("ber_encode_PrivateKeyInfo failed\n");
397a93
+    }
397a93
+
397a93
+error:
397a93
+    if (buf3)
397a93
+        free(buf3);
397a93
+    if (buf2)
397a93
+        free(buf2);
397a93
+    if (buf)
397a93
+        free(buf);
397a93
+    if (algid)
397a93
+        free(algid);
397a93
+    if (pk_rs)
397a93
+        free(pk_rs);
397a93
+
397a93
+    return rc;
397a93
+}
397a93
+
397a93
+/**
397a93
+ * decode an IBM Kyber private key:
397a93
+ *
397a93
+ *     KyberPrivateKey ::= SEQUENCE {
397a93
+ *       version INTEGER,     -- v0, reserved 0
397a93
+ *       sk BIT STRING,       -- private key
397a93
+ *       pk [0] IMPLICIT OPTIONAL {
397a93
+ *         pk||rs BIT STRING  -- public key (pk) concatenated with 2x32 bytes rs
397a93
+ *       }
397a93
+ *     }
397a93
+ */
397a93
+CK_RV ber_decode_IBM_KyberPrivateKey(CK_BYTE *data,
397a93
+                                     CK_ULONG data_len,
397a93
+                                     CK_ATTRIBUTE **sk,
397a93
+                                     CK_ATTRIBUTE **pk,
397a93
+                                     CK_ATTRIBUTE **value,
397a93
+                                     const struct pqc_oid **oid)
397a93
+{
397a93
+    CK_ATTRIBUTE *sk_attr = NULL, *pk_attr = NULL, *value_attr = NULL;
397a93
+    CK_BYTE *algoid = NULL;
397a93
+    CK_BYTE *kyber_priv_key = NULL;
397a93
+    CK_BYTE *buf = NULL;
397a93
+    CK_BYTE *tmp = NULL;
397a93
+    CK_ULONG offset, buf_len, field_len, len, option;
397a93
+    CK_RV rc;
397a93
+
397a93
+    /* Check if this is a Kyber private key */
397a93
+    rc = ber_decode_PrivateKeyInfo(data, data_len, &algoid, &len,
397a93
+                                   &kyber_priv_key);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_DEVEL("ber_decode_PrivateKeyInfo failed\n");
397a93
+        return rc;
397a93
+    }
397a93
+
397a93
+    if (len <= ber_NULLLen ||
397a93
+        memcmp(algoid + len - ber_NULLLen, ber_NULL, ber_NULLLen) != 0) {
397a93
+        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
397a93
+        return CKR_FUNCTION_FAILED;
397a93
+    }
397a93
+    len -= ber_NULLLen;
397a93
+    *oid = find_pqc_by_oid(kyber_oids, algoid, len);
397a93
+    if (*oid == NULL) {
397a93
+        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
397a93
+        return CKR_FUNCTION_FAILED;
397a93
+    }
397a93
+
397a93
+    /* Decode private Kyber key */
397a93
+    rc = ber_decode_SEQUENCE(kyber_priv_key, &buf, &buf_len, &field_len);
397a93
+    if (rc != CKR_OK)
397a93
+        return rc;
397a93
+
397a93
+    /* Now build the attributes */
397a93
+    offset = 0;
397a93
+
397a93
+    /* Skip the version */
397a93
+    rc = ber_decode_INTEGER(buf + offset, &tmp, &len, &field_len);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_DEVEL("ber_decode_INTEGER failed\n");
397a93
+        goto cleanup;
397a93
+    }
397a93
+    offset += field_len;
397a93
+
397a93
+    /* sk */
397a93
+    rc = ber_decode_BIT_STRING(buf + offset, &tmp, &len, &field_len);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_DEVEL("ber_decode_BIT_STRING of (sk) failed\n");
397a93
+        goto cleanup;
397a93
+    } else {
397a93
+        tmp++; /* Remove unused-bits byte */
397a93
+        len--;
397a93
+        rc = build_attribute(CKA_IBM_KYBER_SK, tmp, len, &sk_attr);
397a93
+        if (rc != CKR_OK) {
397a93
+            TRACE_DEVEL("build_attribute for (sk) failed\n");
397a93
+            goto cleanup;
397a93
+        }
397a93
+        offset += field_len;
397a93
+    }
397a93
+
397a93
+    /* pk (optional, within choice) */
397a93
+    if (offset < buf_len) {
397a93
+        rc = ber_decode_CHOICE(buf + offset, &tmp, &len, &field_len, &option);
397a93
+        if (rc != CKR_OK) {
397a93
+            TRACE_DEVEL("ber_decode_BIT_STRING of (t1) failed\n");
397a93
+            goto cleanup;
397a93
+        }
397a93
+
397a93
+        if (option != 0x00) {
397a93
+            TRACE_DEVEL("ber_decode_CHOICE returned invalid option %ld\n",
397a93
+                        option);
397a93
+            goto cleanup;
397a93
+        }
397a93
+
397a93
+        offset += field_len - len;
397a93
+
397a93
+        rc = ber_decode_BIT_STRING(buf + offset, &tmp, &len, &field_len);
397a93
+        if (rc != CKR_OK) {
397a93
+            TRACE_DEVEL("ber_decode_BIT_STRING of (pk) failed\n");
397a93
+            goto cleanup;
397a93
+        }
397a93
+        tmp++; /* Remove unused-bits byte */
397a93
+        len--;
397a93
+
397a93
+        if (len > 64)
397a93
+            len -= 64; /* Remove 'rs' */
397a93
+
397a93
+        rc = build_attribute(CKA_IBM_KYBER_PK, tmp, len, &pk_attr);
397a93
+        if (rc != CKR_OK) {
397a93
+            TRACE_DEVEL("build_attribute for (pk) failed\n");
397a93
+            goto cleanup;
397a93
+        }
397a93
+        offset += field_len;
397a93
+    }
397a93
+
397a93
+    /* Check if buffer big enough */
397a93
+    if (offset > buf_len) {
397a93
+        TRACE_ERROR("%s\n", ock_err(ERR_FUNCTION_FAILED));
397a93
+        rc = CKR_FUNCTION_FAILED;
397a93
+        goto cleanup;
397a93
+    }
397a93
+
397a93
+    /* Add private key as CKA_VALUE to public key (z/OS ICSF compatibility) */
397a93
+    rc = ber_decode_SEQUENCE(data, &tmp, &len, &field_len);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("%s ber_decode_SEQUENCE failed with rc=0x%lx\n", __func__, rc);
397a93
+        goto cleanup;
397a93
+    }
397a93
+    rc = build_attribute(CKA_VALUE, data, field_len, &value_attr);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_DEVEL("build_attribute for (t1) failed\n");
397a93
+        goto cleanup;
397a93
+    }
397a93
+
397a93
+    *sk = sk_attr;
397a93
+    *pk = pk_attr;
397a93
+    *value = value_attr;
397a93
+
397a93
+    return CKR_OK;
397a93
+
397a93
+cleanup:
397a93
+
397a93
+    if (sk_attr)
397a93
+        free(sk_attr);
397a93
+    if (pk_attr)
397a93
+        free(pk_attr);
397a93
+    if (value_attr)
397a93
+        free(value_attr);
397a93
+
397a93
+    return rc;
397a93
+}
397a93
+
397a93
diff --git a/usr/lib/common/h_extern.h b/usr/lib/common/h_extern.h
397a93
index fdbcacd9..ee1ae08d 100644
397a93
--- a/usr/lib/common/h_extern.h
397a93
+++ b/usr/lib/common/h_extern.h
397a93
@@ -2505,6 +2505,40 @@ CK_RV ibm_dilithium_priv_unwrap(TEMPLATE *tmpl, CK_BYTE *data,
397a93
 CK_RV ibm_dilithium_priv_unwrap_get_data(TEMPLATE *tmpl,
397a93
                                          CK_BYTE *data, CK_ULONG total_length,
397a93
                                          CK_BBOOL add_value);
397a93
+
397a93
+// Kyber routines
397a93
+//
397a93
+CK_RV ibm_kyber_publ_check_required_attributes(TEMPLATE *tmpl, CK_ULONG mode);
397a93
+CK_RV ibm_kyber_publ_set_default_attributes(TEMPLATE *tmpl, CK_ULONG mode);
397a93
+CK_RV ibm_kyber_publ_validate_attribute(STDLL_TokData_t *tokdata, TEMPLATE *tmpl,
397a93
+                                        CK_ATTRIBUTE *attr, CK_ULONG mode);
397a93
+CK_RV ibm_kyber_publ_get_spki(TEMPLATE *tmpl, CK_BBOOL length_only,
397a93
+                              CK_BYTE **data, CK_ULONG *data_len);
397a93
+CK_RV ibm_kyber_priv_check_required_attributes(TEMPLATE *tmpl, CK_ULONG mode);
397a93
+CK_RV ibm_kyber_priv_set_default_attributes(TEMPLATE *tmpl, CK_ULONG mode);
397a93
+CK_RV ibm_kyber_priv_validate_attribute(STDLL_TokData_t *tokdata, TEMPLATE *tmpl,
397a93
+                                        CK_ATTRIBUTE *attr, CK_ULONG mode);
397a93
+CK_RV ibm_kyber_priv_wrap_get_data(TEMPLATE *tmpl, CK_BBOOL length_only,
397a93
+                                   CK_BYTE **data, CK_ULONG *data_len);
397a93
+CK_RV ibm_kyber_priv_unwrap(TEMPLATE *tmpl, CK_BYTE *data,
397a93
+                            CK_ULONG total_length, CK_BBOOL add_value);
397a93
+CK_RV ibm_kyber_priv_unwrap_get_data(TEMPLATE *tmpl,
397a93
+                                     CK_BYTE *data, CK_ULONG total_length,
397a93
+                                     CK_BBOOL add_value);
397a93
+
397a93
+// PQC helper routines
397a93
+//
397a93
+CK_RV ibm_pqc_publ_get_spki(TEMPLATE *tmpl, CK_KEY_TYPE keytype,
397a93
+                            CK_BBOOL length_only,
397a93
+                            CK_BYTE **data, CK_ULONG *data_len);
397a93
+CK_RV ibm_pqc_priv_wrap_get_data(TEMPLATE *tmpl, CK_KEY_TYPE keytype,
397a93
+                                 CK_BBOOL length_only,
397a93
+                                 CK_BYTE **data, CK_ULONG *data_len);
397a93
+CK_RV ibm_pqc_priv_unwrap(TEMPLATE *tmpl, CK_KEY_TYPE keytype, CK_BYTE *data,
397a93
+                          CK_ULONG total_length, CK_BBOOL add_value);
397a93
+CK_RV ibm_pqc_priv_unwrap_get_data(TEMPLATE *tmpl, CK_KEY_TYPE keytype,
397a93
+                                   CK_BYTE *data, CK_ULONG total_length,
397a93
+                                   CK_BBOOL add_value);
397a93
 const struct pqc_oid *ibm_pqc_get_keyform_mode(TEMPLATE *tmpl,
397a93
                                                CK_MECHANISM_TYPE mech);
397a93
 CK_RV ibm_pqc_add_keyform_mode(TEMPLATE *tmpl, const struct pqc_oid *oid,
397a93
@@ -2782,6 +2816,31 @@ CK_RV ber_decode_IBM_DilithiumPrivateKey(CK_BYTE *data,
397a93
                                          CK_ATTRIBUTE **value,
397a93
                                          const struct pqc_oid **oid);
397a93
 
397a93
+CK_RV ber_encode_IBM_KyberPublicKey(CK_BBOOL length_only,
397a93
+                                    CK_BYTE **data, CK_ULONG *data_len,
397a93
+                                    const CK_BYTE *oid, CK_ULONG oid_len,
397a93
+                                    CK_ATTRIBUTE *pk);
397a93
+
397a93
+CK_RV ber_decode_IBM_KyberPublicKey(CK_BYTE *data,
397a93
+                                    CK_ULONG data_len,
397a93
+                                    CK_ATTRIBUTE **pk_attr,
397a93
+                                    CK_ATTRIBUTE **value_attr,
397a93
+                                    const struct pqc_oid **oid);
397a93
+
397a93
+CK_RV ber_encode_IBM_KyberPrivateKey(CK_BBOOL length_only,
397a93
+                                     CK_BYTE **data,
397a93
+                                     CK_ULONG *data_len,
397a93
+                                     const CK_BYTE *oid, CK_ULONG oid_len,
397a93
+                                     CK_ATTRIBUTE *sk,
397a93
+                                     CK_ATTRIBUTE *pk);
397a93
+
397a93
+CK_RV ber_decode_IBM_KyberPrivateKey(CK_BYTE *data,
397a93
+                                     CK_ULONG data_len,
397a93
+                                     CK_ATTRIBUTE **sk,
397a93
+                                     CK_ATTRIBUTE **pk,
397a93
+                                     CK_ATTRIBUTE **value,
397a93
+                                     const struct pqc_oid **oid);
397a93
+
397a93
 typedef CK_RV (*t_rsa_encrypt)(STDLL_TokData_t *, CK_BYTE *in_data,
397a93
                                CK_ULONG in_data_len, CK_BYTE *out_data,
397a93
                                OBJECT *key_obj);
397a93
diff --git a/usr/lib/common/key.c b/usr/lib/common/key.c
397a93
index ba40cefd..ef329452 100644
397a93
--- a/usr/lib/common/key.c
397a93
+++ b/usr/lib/common/key.c
397a93
@@ -722,6 +722,9 @@ CK_RV publ_key_get_spki(TEMPLATE *tmpl, CK_ULONG keytype, CK_BBOOL length_only,
397a93
     case CKK_IBM_PQC_DILITHIUM:
397a93
         rc = ibm_dilithium_publ_get_spki(tmpl, length_only, data, data_len);
397a93
         break;
397a93
+    case CKK_IBM_PQC_KYBER:
397a93
+        rc = ibm_kyber_publ_get_spki(tmpl, length_only, data, data_len);
397a93
+        break;
397a93
     default:
397a93
         TRACE_ERROR("%s\n", ock_err(ERR_KEY_TYPE_INCONSISTENT));
397a93
         return CKR_KEY_TYPE_INCONSISTENT;
397a93
@@ -1053,6 +1056,9 @@ CK_RV priv_key_unwrap(TEMPLATE *tmpl,
397a93
     case CKK_IBM_PQC_DILITHIUM:
397a93
         rc = ibm_dilithium_priv_unwrap(tmpl, data, data_len, TRUE);
397a93
         break;
397a93
+    case CKK_IBM_PQC_KYBER:
397a93
+        rc = ibm_kyber_priv_unwrap(tmpl, data, data_len, TRUE);
397a93
+        break;
397a93
     default:
397a93
         TRACE_ERROR("%s\n", ock_err(ERR_WRAPPED_KEY_INVALID));
397a93
         return CKR_WRAPPED_KEY_INVALID;
397a93
@@ -3030,6 +3036,240 @@ error:
397a93
     return rc;
397a93
 }
397a93
 
397a93
+/*
397a93
+ * Extract the SubjectPublicKeyInfo from the Kyber public key
397a93
+ */
397a93
+CK_RV ibm_kyber_publ_get_spki(TEMPLATE *tmpl, CK_BBOOL length_only,
397a93
+                              CK_BYTE **data, CK_ULONG *data_len)
397a93
+{
397a93
+    CK_ATTRIBUTE *pk = NULL;
397a93
+    const struct pqc_oid *oid;
397a93
+    CK_RV rc;
397a93
+
397a93
+    oid = ibm_pqc_get_keyform_mode(tmpl, CKM_IBM_KYBER);
397a93
+    if (oid == NULL)
397a93
+       return CKR_TEMPLATE_INCOMPLETE;
397a93
+
397a93
+    rc = template_attribute_get_non_empty(tmpl, CKA_IBM_KYBER_PK, &pk;;
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("Could not find CKA_IBM_KYBER_PK for the key.\n");
397a93
+        return rc;
397a93
+    }
397a93
+
397a93
+    rc = ber_encode_IBM_KyberPublicKey(length_only, data, data_len,
397a93
+                                       oid->oid, oid->oid_len, pk);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("ber_encode_IBM_KyberPublicKey failed.\n");
397a93
+        return rc;
397a93
+    }
397a93
+
397a93
+    return CKR_OK;
397a93
+}
397a93
+
397a93
+
397a93
+CK_RV ibm_kyber_priv_wrap_get_data(TEMPLATE *tmpl,
397a93
+                                   CK_BBOOL length_only,
397a93
+                                   CK_BYTE **data, CK_ULONG *data_len)
397a93
+{
397a93
+    CK_ATTRIBUTE *sk = NULL, *pk = NULL;
397a93
+    const struct pqc_oid *oid;
397a93
+    CK_RV rc;
397a93
+
397a93
+    oid = ibm_pqc_get_keyform_mode(tmpl, CKM_IBM_KYBER);
397a93
+    if (oid == NULL)
397a93
+       return CKR_TEMPLATE_INCOMPLETE;
397a93
+
397a93
+    rc = template_attribute_get_non_empty(tmpl, CKA_IBM_KYBER_SK, &sk;;
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("Could not find CKA_IBM_KYBER_SK for the key.\n");
397a93
+        return rc;
397a93
+    }
397a93
+
397a93
+    rc = template_attribute_get_non_empty(tmpl, CKA_IBM_KYBER_PK, &pk;;
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("Could not find CKA_IBM_KYBER_PK for the key.\n");
397a93
+        return rc;
397a93
+    }
397a93
+
397a93
+    rc = ber_encode_IBM_KyberPrivateKey(length_only, data, data_len,
397a93
+                                        oid->oid, oid->oid_len, sk, pk);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_DEVEL("ber_encode_IBM_KyberPrivateKey failed\n");
397a93
+    }
397a93
+
397a93
+    return rc;
397a93
+}
397a93
+
397a93
+CK_RV ibm_kyber_priv_unwrap_get_data(TEMPLATE *tmpl, CK_BYTE *data,
397a93
+                                     CK_ULONG total_length,
397a93
+                                     CK_BBOOL add_value)
397a93
+{
397a93
+    CK_ATTRIBUTE *pk = NULL;
397a93
+    CK_ATTRIBUTE *value = NULL;
397a93
+    const struct pqc_oid *oid;
397a93
+    CK_RV rc;
397a93
+
397a93
+    rc = ber_decode_IBM_KyberPublicKey(data, total_length, &pk,
397a93
+                                       &value, &oid;;
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("ber_decode_IBM_KyberPublicKey failed\n");
397a93
+        return rc;
397a93
+    }
397a93
+
397a93
+    rc = ibm_pqc_add_keyform_mode(tmpl, oid, CKM_IBM_KYBER);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("ibm_pqc_add_keyform_mode failed\n");
397a93
+        return rc;
397a93
+    }
397a93
+
397a93
+    rc = template_update_attribute(tmpl, pk);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_DEVEL("template_update_attribute failed.\n");
397a93
+        goto error;
397a93
+    }
397a93
+    pk = NULL;
397a93
+    if (add_value) {
397a93
+        rc = template_update_attribute(tmpl, value);
397a93
+        if (rc != CKR_OK) {
397a93
+            TRACE_DEVEL("template_update_attribute failed.\n");
397a93
+            goto error;
397a93
+        }
397a93
+    } else {
397a93
+        free(value);
397a93
+    }
397a93
+    value = NULL;
397a93
+
397a93
+    return CKR_OK;
397a93
+
397a93
+error:
397a93
+    if (pk)
397a93
+        free(pk);
397a93
+    if (value)
397a93
+        free(value);
397a93
+
397a93
+    return rc;
397a93
+}
397a93
+
397a93
+//
397a93
+//
397a93
+CK_RV ibm_kyber_priv_unwrap(TEMPLATE *tmpl, CK_BYTE *data,
397a93
+                            CK_ULONG total_length, CK_BBOOL add_value)
397a93
+{
397a93
+    CK_ATTRIBUTE *sk = NULL, *pk = NULL, *value = NULL;
397a93
+    const struct pqc_oid *oid;
397a93
+    CK_RV rc;
397a93
+
397a93
+    rc = ber_decode_IBM_KyberPrivateKey(data, total_length,
397a93
+                                        &sk, &pk, &value, &oid;;
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("ber_decode_IBM_KyberPrivateKey failed\n");
397a93
+        return rc;
397a93
+    }
397a93
+
397a93
+    rc = ibm_pqc_add_keyform_mode(tmpl, oid, CKM_IBM_KYBER);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("ibm_pqc_add_keyform_mode failed\n");
397a93
+        return rc;
397a93
+    }
397a93
+
397a93
+    rc = template_update_attribute(tmpl, sk);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("template_update_attribute failed\n");
397a93
+        goto error;
397a93
+    }
397a93
+    sk = NULL;
397a93
+    rc = template_update_attribute(tmpl, pk);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("template_update_attribute failed\n");
397a93
+        goto error;
397a93
+    }
397a93
+    pk = NULL;
397a93
+    if (add_value) {
397a93
+        rc = template_update_attribute(tmpl, value);
397a93
+        if (rc != CKR_OK) {
397a93
+            TRACE_DEVEL("template_update_attribute failed.\n");
397a93
+            goto error;
397a93
+        }
397a93
+    } else {
397a93
+        free(value);
397a93
+    }
397a93
+    value = NULL;
397a93
+
397a93
+    return CKR_OK;
397a93
+
397a93
+error:
397a93
+    if (sk)
397a93
+        free(sk);
397a93
+    if (pk)
397a93
+        free(pk);
397a93
+    if (value)
397a93
+        free(value);
397a93
+
397a93
+    return rc;
397a93
+}
397a93
+
397a93
+CK_RV ibm_pqc_publ_get_spki(TEMPLATE *tmpl, CK_KEY_TYPE keytype,
397a93
+                            CK_BBOOL length_only,
397a93
+                            CK_BYTE **data, CK_ULONG *data_len)
397a93
+{
397a93
+    switch (keytype) {
397a93
+    case CKK_IBM_PQC_DILITHIUM:
397a93
+        return ibm_dilithium_publ_get_spki(tmpl, length_only, data, data_len);
397a93
+    case CKK_IBM_PQC_KYBER:
397a93
+        return ibm_kyber_publ_get_spki(tmpl, length_only, data, data_len);
397a93
+    default:
397a93
+        TRACE_DEVEL("Key type 0x%lx not supported.\n", keytype);
397a93
+        return CKR_KEY_TYPE_INCONSISTENT;
397a93
+    }
397a93
+}
397a93
+
397a93
+CK_RV ibm_pqc_priv_wrap_get_data(TEMPLATE *tmpl, CK_KEY_TYPE keytype,
397a93
+                                 CK_BBOOL length_only,
397a93
+                                 CK_BYTE **data, CK_ULONG *data_len)
397a93
+{
397a93
+    switch (keytype) {
397a93
+    case CKK_IBM_PQC_DILITHIUM:
397a93
+        return ibm_dilithium_priv_wrap_get_data(tmpl, length_only, data,
397a93
+                                                data_len);
397a93
+    case CKK_IBM_PQC_KYBER:
397a93
+        return ibm_kyber_priv_wrap_get_data(tmpl, length_only, data, data_len);
397a93
+    default:
397a93
+        TRACE_DEVEL("Key type 0x%lx not supported.\n", keytype);
397a93
+        return CKR_KEY_TYPE_INCONSISTENT;
397a93
+    }
397a93
+}
397a93
+
397a93
+CK_RV ibm_pqc_priv_unwrap(TEMPLATE *tmpl, CK_KEY_TYPE keytype, CK_BYTE *data,
397a93
+                          CK_ULONG total_length, CK_BBOOL add_value)
397a93
+{
397a93
+    switch (keytype) {
397a93
+    case CKK_IBM_PQC_DILITHIUM:
397a93
+        return ibm_dilithium_priv_unwrap(tmpl, data, total_length, add_value);
397a93
+    case CKK_IBM_PQC_KYBER:
397a93
+        return ibm_kyber_priv_unwrap(tmpl, data, total_length, add_value);
397a93
+    default:
397a93
+        TRACE_DEVEL("Key type 0x%lx not supported.\n", keytype);
397a93
+        return CKR_KEY_TYPE_INCONSISTENT;
397a93
+    }
397a93
+}
397a93
+
397a93
+CK_RV ibm_pqc_priv_unwrap_get_data(TEMPLATE *tmpl, CK_KEY_TYPE keytype,
397a93
+                                   CK_BYTE *data, CK_ULONG total_length,
397a93
+                                   CK_BBOOL add_value)
397a93
+{
397a93
+    switch (keytype) {
397a93
+    case CKK_IBM_PQC_DILITHIUM:
397a93
+        return ibm_dilithium_priv_unwrap_get_data(tmpl, data, total_length,
397a93
+                                                  add_value);
397a93
+    case CKK_IBM_PQC_KYBER:
397a93
+        return ibm_kyber_priv_unwrap_get_data(tmpl, data, total_length,
397a93
+                                                   add_value);
397a93
+    default:
397a93
+        TRACE_DEVEL("Key type 0x%lx not supported.\n", keytype);
397a93
+        return CKR_KEY_TYPE_INCONSISTENT;
397a93
+    }
397a93
+}
397a93
+
397a93
 // dsa_publ_check_required_attributes()
397a93
 //
397a93
 CK_RV dsa_publ_check_required_attributes(TEMPLATE *tmpl, CK_ULONG mode)
397a93
@@ -4987,6 +5227,152 @@ error:
397a93
     return rc;
397a93
 }
397a93
 
397a93
+//  ibm_dilithium_publ_set_default_attributes()
397a93
+//
397a93
+CK_RV ibm_kyber_publ_set_default_attributes(TEMPLATE *tmpl, CK_ULONG mode)
397a93
+{
397a93
+    CK_ATTRIBUTE *type_attr = NULL;
397a93
+    CK_ATTRIBUTE *pk_attr = NULL;
397a93
+    CK_ATTRIBUTE *value_attr = NULL;
397a93
+    CK_RV rc;
397a93
+
397a93
+    publ_key_set_default_attributes(tmpl, mode);
397a93
+
397a93
+    type_attr = (CK_ATTRIBUTE *) malloc(sizeof(CK_ATTRIBUTE) + sizeof(CK_KEY_TYPE));
397a93
+    pk_attr = (CK_ATTRIBUTE *) malloc(sizeof(CK_ATTRIBUTE));
397a93
+    value_attr = (CK_ATTRIBUTE *) malloc(sizeof(CK_ATTRIBUTE));
397a93
+
397a93
+    if (!type_attr || !pk_attr ||!value_attr) {
397a93
+        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
397a93
+        rc = CKR_HOST_MEMORY;
397a93
+        goto error;
397a93
+    }
397a93
+
397a93
+    type_attr->type = CKA_KEY_TYPE;
397a93
+    type_attr->ulValueLen = sizeof(CK_KEY_TYPE);
397a93
+    type_attr->pValue = (CK_BYTE *) type_attr + sizeof(CK_ATTRIBUTE);
397a93
+    *(CK_KEY_TYPE *) type_attr->pValue = CKK_IBM_PQC_KYBER;
397a93
+
397a93
+    pk_attr->type = CKA_IBM_KYBER_PK;
397a93
+    pk_attr->ulValueLen = 0;
397a93
+    pk_attr->pValue = NULL;
397a93
+
397a93
+    value_attr->type = CKA_VALUE;
397a93
+    value_attr->ulValueLen = 0;
397a93
+    value_attr->pValue = NULL;
397a93
+
397a93
+    rc = template_update_attribute(tmpl, type_attr);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("template_update_attribute failed\n");
397a93
+        goto error;
397a93
+    }
397a93
+    type_attr = NULL;
397a93
+    rc = template_update_attribute(tmpl, pk_attr);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("template_update_attribute failed\n");
397a93
+        goto error;
397a93
+    }
397a93
+    pk_attr = NULL;
397a93
+    rc = template_update_attribute(tmpl, value_attr);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("template_update_attribute failed\n");
397a93
+        goto error;
397a93
+    }
397a93
+    value_attr = NULL;
397a93
+
397a93
+    return CKR_OK;
397a93
+
397a93
+error:
397a93
+    if (type_attr)
397a93
+        free(type_attr);
397a93
+    if (pk_attr)
397a93
+        free(pk_attr);
397a93
+    if (value_attr)
397a93
+        free(value_attr);
397a93
+
397a93
+   return rc;
397a93
+}
397a93
+
397a93
+//  ibm_dilithium_priv_set_default_attributes()
397a93
+//
397a93
+CK_RV ibm_kyber_priv_set_default_attributes(TEMPLATE *tmpl, CK_ULONG mode)
397a93
+{
397a93
+    CK_ATTRIBUTE *type_attr = NULL;
397a93
+    CK_ATTRIBUTE *sk_attr = NULL;
397a93
+    CK_ATTRIBUTE *pk_attr = NULL;
397a93
+    CK_ATTRIBUTE *value_attr = NULL;
397a93
+    CK_RV rc;
397a93
+
397a93
+    priv_key_set_default_attributes(tmpl, mode);
397a93
+
397a93
+    type_attr = (CK_ATTRIBUTE *) malloc(sizeof(CK_ATTRIBUTE) + sizeof(CK_KEY_TYPE));
397a93
+    sk_attr = (CK_ATTRIBUTE *) malloc(sizeof(CK_ATTRIBUTE));
397a93
+    pk_attr = (CK_ATTRIBUTE *) malloc(sizeof(CK_ATTRIBUTE));
397a93
+    value_attr = (CK_ATTRIBUTE *) malloc(sizeof(CK_ATTRIBUTE));
397a93
+
397a93
+    if (!type_attr || !sk_attr || !pk_attr || !value_attr) {
397a93
+        TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
397a93
+        rc = CKR_HOST_MEMORY;
397a93
+        goto error;
397a93
+    }
397a93
+
397a93
+    type_attr->type = CKA_KEY_TYPE;
397a93
+    type_attr->ulValueLen = sizeof(CK_KEY_TYPE);
397a93
+    type_attr->pValue = (CK_BYTE *) type_attr + sizeof(CK_ATTRIBUTE);
397a93
+    *(CK_KEY_TYPE *) type_attr->pValue = CKK_IBM_PQC_KYBER;
397a93
+
397a93
+    sk_attr->type = CKA_IBM_KYBER_SK;
397a93
+    sk_attr->ulValueLen = 0;
397a93
+    sk_attr->pValue = NULL;
397a93
+
397a93
+    pk_attr->type = CKA_IBM_KYBER_PK;
397a93
+    pk_attr->ulValueLen = 0;
397a93
+    pk_attr->pValue = NULL;
397a93
+
397a93
+    value_attr->type = CKA_VALUE;
397a93
+    value_attr->ulValueLen = 0;
397a93
+    value_attr->pValue = NULL;
397a93
+
397a93
+    rc = template_update_attribute(tmpl, type_attr);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("template_update_attribute failed\n");
397a93
+        goto error;
397a93
+    }
397a93
+    type_attr = NULL;
397a93
+    rc = template_update_attribute(tmpl, sk_attr);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("template_update_attribute failed\n");
397a93
+        goto error;
397a93
+    }
397a93
+    sk_attr = NULL;
397a93
+    rc = template_update_attribute(tmpl, pk_attr);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("template_update_attribute failed\n");
397a93
+        goto error;
397a93
+    }
397a93
+    pk_attr = NULL;
397a93
+    rc = template_update_attribute(tmpl, value_attr);
397a93
+    if (rc != CKR_OK) {
397a93
+        TRACE_ERROR("template_update_attribute failed\n");
397a93
+        goto error;
397a93
+    }
397a93
+    value_attr = NULL;
397a93
+
397a93
+    return CKR_OK;
397a93
+
397a93
+error:
397a93
+    if (type_attr)
397a93
+        free(type_attr);
397a93
+    if (sk_attr)
397a93
+        free(sk_attr);
397a93
+    if (pk_attr)
397a93
+        free(pk_attr);
397a93
+    if (value_attr)
397a93
+        free(value_attr);
397a93
+
397a93
+    return rc;
397a93
+}
397a93
+
397a93
 static CK_RV ibm_pqc_check_attributes(TEMPLATE *tmpl, CK_ULONG mode,
397a93
                                       CK_MECHANISM_TYPE mech,
397a93
                                       CK_ULONG *req_attrs,
397a93
@@ -5122,6 +5508,43 @@ CK_RV ibm_dilithium_priv_check_required_attributes(TEMPLATE *tmpl, CK_ULONG mode
397a93
     return priv_key_check_required_attributes(tmpl, mode);
397a93
 }
397a93
 
397a93
+// ibm_kyber_publ_check_required_attributes()
397a93
+//
397a93
+CK_RV ibm_kyber_publ_check_required_attributes(TEMPLATE *tmpl, CK_ULONG mode)
397a93
+{
397a93
+    static CK_ULONG req_attrs[] = {
397a93
+        CKA_IBM_KYBER_PK,
397a93
+    };
397a93
+    CK_RV rc;
397a93
+
397a93
+    rc = ibm_pqc_check_attributes(tmpl, mode, CKM_IBM_KYBER, req_attrs,
397a93
+                                  sizeof(req_attrs) / sizeof(req_attrs[0]));
397a93
+    if (rc != CKR_OK)
397a93
+        return rc;
397a93
+
397a93
+    /* All required attrs found, check them */
397a93
+    return publ_key_check_required_attributes(tmpl, mode);
397a93
+}
397a93
+
397a93
+// ibm_kyber_priv_check_required_attributes()
397a93
+//
397a93
+CK_RV ibm_kyber_priv_check_required_attributes(TEMPLATE *tmpl, CK_ULONG mode)
397a93
+{
397a93
+    static CK_ULONG req_attrs[] = {
397a93
+        CKA_IBM_KYBER_SK,
397a93
+        CKA_IBM_KYBER_PK,
397a93
+    };
397a93
+    CK_RV rc;
397a93
+
397a93
+    rc = ibm_pqc_check_attributes(tmpl, mode, CKM_IBM_KYBER, req_attrs,
397a93
+                                  sizeof(req_attrs) / sizeof(req_attrs[0]));
397a93
+    if (rc != CKR_OK)
397a93
+        return rc;
397a93
+
397a93
+    /* All required attrs found, check them */
397a93
+    return priv_key_check_required_attributes(tmpl, mode);
397a93
+}
397a93
+
397a93
 static CK_RV ibm_pqc_validate_keyform_mode(CK_ATTRIBUTE *attr, CK_ULONG mode,
397a93
                                            CK_MECHANISM_TYPE mech)
397a93
 {
397a93
@@ -5228,6 +5651,59 @@ CK_RV ibm_dilithium_priv_validate_attribute(STDLL_TokData_t *tokdata,
397a93
     }
397a93
 }
397a93
 
397a93
+// ibm_kyber_publ_validate_attribute()
397a93
+//
397a93
+CK_RV ibm_kyber_publ_validate_attribute(STDLL_TokData_t *tokdata,
397a93
+                                        TEMPLATE *tmpl, CK_ATTRIBUTE *attr,
397a93
+                                        CK_ULONG mode)
397a93
+{
397a93
+    CK_RV rc;
397a93
+
397a93
+    switch (attr->type) {
397a93
+    case CKA_IBM_KYBER_KEYFORM:
397a93
+    case CKA_IBM_KYBER_MODE:
397a93
+        rc = ibm_pqc_validate_keyform_mode(attr, mode, CKM_IBM_KYBER);
397a93
+        if (rc != CKR_OK)
397a93
+            return rc;
397a93
+        return CKR_OK;
397a93
+    case CKA_IBM_KYBER_PK:
397a93
+    case CKA_VALUE:
397a93
+        if (mode == MODE_CREATE)
397a93
+            return CKR_OK;
397a93
+        TRACE_ERROR("%s\n", ock_err(ERR_ATTRIBUTE_READ_ONLY));
397a93
+        return CKR_ATTRIBUTE_READ_ONLY;
397a93
+    default:
397a93
+        return publ_key_validate_attribute(tokdata, tmpl, attr, mode);
397a93
+    }
397a93
+}
397a93
+
397a93
+// ibm_kyber_priv_validate_attribute()
397a93
+//
397a93
+CK_RV ibm_kyber_priv_validate_attribute(STDLL_TokData_t *tokdata,
397a93
+                                        TEMPLATE *tmpl, CK_ATTRIBUTE *attr,
397a93
+                                        CK_ULONG mode)
397a93
+{
397a93
+    CK_RV rc;
397a93
+
397a93
+    switch (attr->type) {
397a93
+    case CKA_IBM_KYBER_KEYFORM:
397a93
+    case CKA_IBM_KYBER_MODE:
397a93
+        rc = ibm_pqc_validate_keyform_mode(attr, mode, CKM_IBM_KYBER);
397a93
+        if (rc != CKR_OK)
397a93
+            return rc;
397a93
+        return CKR_OK;
397a93
+    case CKA_IBM_KYBER_SK:
397a93
+    case CKA_IBM_KYBER_PK:
397a93
+    case CKA_VALUE:
397a93
+        if (mode == MODE_CREATE)
397a93
+            return CKR_OK;
397a93
+        TRACE_ERROR("%s\n", ock_err(ERR_ATTRIBUTE_READ_ONLY));
397a93
+        return CKR_ATTRIBUTE_READ_ONLY;
397a93
+    default:
397a93
+        return priv_key_validate_attribute(tokdata, tmpl, attr, mode);
397a93
+    }
397a93
+}
397a93
+
397a93
 // generic_secret_check_required_attributes()
397a93
 //
397a93
 CK_RV generic_secret_check_required_attributes(TEMPLATE *tmpl, CK_ULONG mode)
397a93
diff --git a/usr/lib/common/key_mgr.c b/usr/lib/common/key_mgr.c
397a93
index 01103dc2..8fcdc88e 100644
397a93
--- a/usr/lib/common/key_mgr.c
397a93
+++ b/usr/lib/common/key_mgr.c
397a93
@@ -1421,6 +1421,16 @@ CK_RV key_mgr_get_private_key_type(CK_BYTE *keydata,
397a93
             return CKR_OK;
397a93
         }
397a93
     }
397a93
+    // Check only the OBJECT IDENTIFIERs for KYBER
397a93
+    //
397a93
+    for (i = 0; kyber_oids[i].oid != NULL; i++) {
397a93
+        if (alg_len == kyber_oids[i].oid_len + ber_NULLLen &&
397a93
+            memcmp(alg, kyber_oids[i].oid, kyber_oids[i].oid_len) == 0 &&
397a93
+            memcmp(alg + kyber_oids[i].oid_len, ber_NULL, ber_NULLLen) == 0) {
397a93
+            *keytype = CKK_IBM_PQC_KYBER;
397a93
+            return CKR_OK;
397a93
+        }
397a93
+    }
397a93
 
397a93
     TRACE_ERROR("%s\n", ock_err(ERR_TEMPLATE_INCOMPLETE));
397a93
     return CKR_TEMPLATE_INCOMPLETE;
397a93
diff --git a/usr/lib/common/mech_ec.c b/usr/lib/common/mech_ec.c
397a93
index 9df53b46..3a642f50 100644
397a93
--- a/usr/lib/common/mech_ec.c
397a93
+++ b/usr/lib/common/mech_ec.c
397a93
@@ -912,6 +912,9 @@ CK_RV pkcs_get_keytype(CK_ATTRIBUTE *attrs, CK_ULONG attrs_len,
397a93
     case CKM_IBM_DILITHIUM:
397a93
         *type = CKK_IBM_PQC_DILITHIUM;
397a93
         break;
397a93
+    case CKM_IBM_KYBER:
397a93
+        *type = CKK_IBM_PQC_KYBER;
397a93
+        break;
397a93
     default:
397a93
         return CKR_MECHANISM_INVALID;
397a93
     }
397a93
diff --git a/usr/lib/common/template.c b/usr/lib/common/template.c
397a93
index 3338e847..37831efc 100644
397a93
--- a/usr/lib/common/template.c
397a93
+++ b/usr/lib/common/template.c
397a93
@@ -164,6 +164,8 @@ CK_RV template_add_default_attributes(TEMPLATE *tmpl, TEMPLATE *basetmpl,
397a93
             return dh_publ_set_default_attributes(tmpl, mode);
397a93
         case CKK_IBM_PQC_DILITHIUM:
397a93
             return ibm_dilithium_publ_set_default_attributes(tmpl, mode);
397a93
+        case CKK_IBM_PQC_KYBER:
397a93
+            return ibm_kyber_publ_set_default_attributes(tmpl, mode);
397a93
         default:
397a93
             TRACE_ERROR("%s: %lx\n", ock_err(ERR_ATTRIBUTE_VALUE_INVALID),
397a93
                         subclass);
397a93
@@ -181,6 +183,8 @@ CK_RV template_add_default_attributes(TEMPLATE *tmpl, TEMPLATE *basetmpl,
397a93
             return dh_priv_set_default_attributes(tmpl, mode);
397a93
         case CKK_IBM_PQC_DILITHIUM:
397a93
             return ibm_dilithium_priv_set_default_attributes(tmpl, mode);
397a93
+        case CKK_IBM_PQC_KYBER:
397a93
+            return ibm_kyber_priv_set_default_attributes(tmpl, mode);
397a93
         default:
397a93
             TRACE_ERROR("%s: %lx\n", ock_err(ERR_ATTRIBUTE_VALUE_INVALID),
397a93
                         subclass);
397a93
@@ -409,6 +413,8 @@ CK_RV template_check_required_attributes(TEMPLATE *tmpl, CK_ULONG class,
397a93
             return dh_publ_check_required_attributes(tmpl, mode);
397a93
         case CKK_IBM_PQC_DILITHIUM:
397a93
             return ibm_dilithium_publ_check_required_attributes(tmpl, mode);
397a93
+        case CKK_IBM_PQC_KYBER:
397a93
+            return ibm_kyber_publ_check_required_attributes(tmpl, mode);
397a93
         default:
397a93
             TRACE_ERROR("%s: %lx\n", ock_err(ERR_ATTRIBUTE_VALUE_INVALID),
397a93
                         subclass);
397a93
@@ -426,6 +432,8 @@ CK_RV template_check_required_attributes(TEMPLATE *tmpl, CK_ULONG class,
397a93
             return dh_priv_check_required_attributes(tmpl, mode);
397a93
         case CKK_IBM_PQC_DILITHIUM:
397a93
             return ibm_dilithium_priv_check_required_attributes(tmpl, mode);
397a93
+        case CKK_IBM_PQC_KYBER:
397a93
+            return ibm_kyber_priv_check_required_attributes(tmpl, mode);
397a93
         default:
397a93
             TRACE_ERROR("%s: %lx\n", ock_err(ERR_ATTRIBUTE_VALUE_INVALID),
397a93
                         subclass);
397a93
@@ -1642,6 +1650,8 @@ CK_RV template_validate_attribute(STDLL_TokData_t *tokdata, TEMPLATE *tmpl,
397a93
             return dh_publ_validate_attribute(tokdata, tmpl, attr, mode);
397a93
         case CKK_IBM_PQC_DILITHIUM:
397a93
             return ibm_dilithium_publ_validate_attribute(tokdata, tmpl, attr, mode);
397a93
+        case CKK_IBM_PQC_KYBER:
397a93
+            return ibm_kyber_publ_validate_attribute(tokdata, tmpl, attr, mode);
397a93
         default:
397a93
             TRACE_ERROR("%s\n", ock_err(ERR_ATTRIBUTE_VALUE_INVALID));
397a93
             return CKR_ATTRIBUTE_VALUE_INVALID; // unknown key type
397a93
@@ -1658,6 +1668,8 @@ CK_RV template_validate_attribute(STDLL_TokData_t *tokdata, TEMPLATE *tmpl,
397a93
             return dh_priv_validate_attribute(tokdata, tmpl, attr, mode);
397a93
         case CKK_IBM_PQC_DILITHIUM:
397a93
             return ibm_dilithium_priv_validate_attribute(tokdata, tmpl, attr, mode);
397a93
+        case CKK_IBM_PQC_KYBER:
397a93
+            return ibm_kyber_priv_validate_attribute(tokdata, tmpl, attr, mode);
397a93
         default:
397a93
             TRACE_ERROR("%s\n", ock_err(ERR_ATTRIBUTE_VALUE_INVALID));
397a93
             return CKR_ATTRIBUTE_VALUE_INVALID; // unknown key type
397a93
diff --git a/usr/lib/ep11_stdll/ep11_specific.c b/usr/lib/ep11_stdll/ep11_specific.c
397a93
index 479951cb..44796dba 100644
397a93
--- a/usr/lib/ep11_stdll/ep11_specific.c
397a93
+++ b/usr/lib/ep11_stdll/ep11_specific.c
397a93
@@ -362,6 +362,13 @@ static const version_req_t ibm_dilithium_req_versions[] = {
397a93
 };
397a93
 #define NUM_DILITHIUM_REQ (sizeof(ibm_dilithium_req_versions) / sizeof(version_req_t))
397a93
 
397a93
+static const CK_VERSION ibm_cex8p_kyber_support = { .major = 8, .minor = 9 };
397a93
+
397a93
+static const version_req_t ibm_kyber_req_versions[] = {
397a93
+        { .card_type = 8, .min_firmware_version = &ibm_cex8p_kyber_support }
397a93
+};
397a93
+#define NUM_KYBER_REQ (sizeof(ibm_kyber_req_versions) / sizeof(version_req_t))
397a93
+
397a93
 static const CK_VERSION ibm_cex6p_reencrypt_single_support =
397a93
                                                     { .major = 6, .minor = 15 };
397a93
 static const CK_VERSION ibm_cex7p_reencrypt_single_support =
397a93
@@ -1809,7 +1816,8 @@ static CK_RV check_key_attributes(STDLL_TokData_t * tokdata,
397a93
             check_types = &check_types_pub[0];
397a93
             attr_cnt = sizeof(check_types_pub) / sizeof(CK_ULONG);
397a93
         }
397a93
-        /* do nothing for CKM_DH_PKCS_KEY_PAIR_GEN and CKK_IBM_PQC_DILITHIUM */
397a93
+        /* do nothing for CKM_DH_PKCS_KEY_PAIR_GEN, CKK_IBM_PQC_DILITHIUM,
397a93
+           and CKK_IBM_PQC_KYBER */
397a93
         break;
397a93
     case CKO_PRIVATE_KEY:
397a93
         if ((kt == CKK_EC) || (kt == CKK_ECDSA) || (kt == CKK_DSA)) {
397a93
@@ -1824,7 +1832,7 @@ static CK_RV check_key_attributes(STDLL_TokData_t * tokdata,
397a93
             check_types = &check_types_derive[0];
397a93
             attr_cnt = sizeof(check_types_derive) / sizeof(CK_ULONG);
397a93
         }
397a93
-        /* Do nothing for CKK_IBM_PQC_DILITHIUM */
397a93
+        /* Do nothing for CKK_IBM_PQC_DILITHIUM and CKK_IBM_PQC_KYBER */
397a93
         break;
397a93
     default:
397a93
         return CKR_OK;
397a93
@@ -2029,6 +2037,13 @@ static CK_BBOOL attr_applicable_for_ep11(STDLL_TokData_t * tokdata,
397a93
             attr->type == CKA_IBM_DILITHIUM_MODE)
397a93
             return CK_FALSE;
397a93
         break;
397a93
+    case CKK_IBM_PQC_KYBER:
397a93
+        if (attr->type == CKA_SIGN || attr->type == CKA_VERIFY ||
397a93
+            attr->type == CKA_WRAP || attr->type == CKA_UNWRAP ||
397a93
+            attr->type == CKA_IBM_KYBER_KEYFORM ||
397a93
+            attr->type == CKA_IBM_KYBER_MODE)
397a93
+            return CK_FALSE;
397a93
+        break;
397a93
     default:
397a93
         break;
397a93
     }
397a93
@@ -9145,6 +9160,10 @@ CK_RV ep11tok_unwrap_key(STDLL_TokData_t * tokdata, SESSION * session,
397a93
             rc = ibm_dilithium_priv_unwrap_get_data(key_obj->template,
397a93
                                                     csum, cslen, FALSE);
397a93
             break;
397a93
+        case CKK_IBM_PQC_KYBER:
397a93
+            rc = ibm_kyber_priv_unwrap_get_data(key_obj->template,
397a93
+                                                csum, cslen, FALSE);
397a93
+            break;
397a93
         }
397a93
 
397a93
         if (rc != 0) {
397a93
@@ -9240,6 +9259,7 @@ static const CK_MECHANISM_TYPE ep11_supported_mech_list[] = {
397a93
     CKM_IBM_EC_X448,
397a93
     CKM_IBM_ED25519_SHA512,
397a93
     CKM_IBM_ED448_SHA3,
397a93
+    CKM_IBM_KYBER,
397a93
     CKM_IBM_SHA3_224,
397a93
     CKM_IBM_SHA3_224_HMAC,
397a93
     CKM_IBM_SHA3_256,
397a93
@@ -9478,6 +9498,7 @@ CK_RV ep11tok_is_mechanism_supported(STDLL_TokData_t *tokdata,
397a93
     CK_VERSION ver1_3 = { .major = 1, .minor = 3 };
397a93
     CK_VERSION ver3 = { .major = 3, .minor = 0 };
397a93
     CK_VERSION ver3_1 = { .major = 3, .minor = 0x10 };
397a93
+    CK_VERSION ver4 = { .major = 4, .minor = 0 };
397a93
     CK_BBOOL found = FALSE;
397a93
     CK_ULONG i;
397a93
     int status;
397a93
@@ -9630,6 +9651,23 @@ CK_RV ep11tok_is_mechanism_supported(STDLL_TokData_t *tokdata,
397a93
         }
397a93
         break;
397a93
 
397a93
+    case CKM_IBM_KYBER:
397a93
+        if (compare_ck_version(&ep11_data->ep11_lib_version, &ver4) < 0) {
397a93
+            TRACE_INFO("%s Mech '%s' banned due to host library version\n",
397a93
+                       __func__, ep11_get_ckm(tokdata, type));
397a93
+            rc = CKR_MECHANISM_INVALID;
397a93
+            goto out;
397a93
+        }
397a93
+        status = check_required_versions(tokdata, ibm_kyber_req_versions,
397a93
+                                         NUM_KYBER_REQ);
397a93
+        if (status != 1) {
397a93
+            TRACE_INFO("%s Mech '%s' banned due to mixed firmware versions\n",
397a93
+                       __func__, ep11_get_ckm(tokdata, type));
397a93
+            rc = CKR_MECHANISM_INVALID;
397a93
+            goto out;
397a93
+        }
397a93
+        break;
397a93
+
397a93
     case CKM_IBM_CPACF_WRAP:
397a93
         if (compare_ck_version(&ep11_data->ep11_lib_version, &ver3) <= 0) {
397a93
             TRACE_INFO("%s Mech '%s' banned due to host library version\n",
397a93
diff --git a/usr/lib/ep11_stdll/ep11cpfilter.conf b/usr/lib/ep11_stdll/ep11cpfilter.conf
397a93
index 9d6a2fc8..4353ec53 100644
397a93
--- a/usr/lib/ep11_stdll/ep11cpfilter.conf
397a93
+++ b/usr/lib/ep11_stdll/ep11cpfilter.conf
397a93
@@ -73,7 +73,7 @@ XCP_CPB_ALG_EC_25519: CKM_IBM_EC_X25519, CKM_IBM_ED25519_SHA512, CKM_IBM_EC_X448
397a93
 XCP_CPB_ALG_NBSI2017: CKM_RSA_PKCS, CKM_SHA1_RSA_PKCS, CKM_SHA224_RSA_PKCS, CKM_SHA256_RSA_PKCS, CKM_SHA384_RSA_PKCS, CKM_SHA512_RSA_PKCS
397a93
 
397a93
 #enable support of Dilithium
397a93
-XCP_CPB_ALG_PQC: CKM_IBM_DILITHIUM
397a93
+XCP_CPB_ALG_PQC: CKM_IBM_DILITHIUM, CKM_IBM_KYBER
397a93
 
397a93
 # enable BTC-related functionality
397a93
 XCP_CPB_BTC: CKM_IBM_BTC_DERIVE
397a93
-- 
397a93
2.16.2.windows.1
397a93