Blob Blame History Raw
From 49ce634ed6fa1fddc2bb34fd0f89c0ea0cc368ee Mon Sep 17 00:00:00 2001
From: Ingo Franzki <ifranzki@linux.ibm.com>
Date: Tue, 1 Mar 2022 09:15:15 +0100
Subject: [PATCH 25/34] COMMON/EP11: Add Kyber key type and mechanism

Kyber requires an EP11 host library of version 4.0 or later,
and a CEX8P crypto card.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
---
 usr/lib/api/mechtable.inc            |   1 +
 usr/lib/api/policy.c                 |   2 +
 usr/lib/common/asn1.c                | 563 +++++++++++++++++++++++++++++++++++
 usr/lib/common/h_extern.h            |  59 ++++
 usr/lib/common/key.c                 | 476 +++++++++++++++++++++++++++++
 usr/lib/common/key_mgr.c             |  10 +
 usr/lib/common/mech_ec.c             |   3 +
 usr/lib/common/template.c            |  12 +
 usr/lib/ep11_stdll/ep11_specific.c   |  42 ++-
 usr/lib/ep11_stdll/ep11cpfilter.conf |   2 +-
 10 files changed, 1167 insertions(+), 3 deletions(-)

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