int wc_EncryptPKCS8Key(byte* key, word32 keySz, byte* out, word32* outSz,
        const char* password, int passwordSz, int vPKCS, int pbeOid,
        int encAlgId, byte* salt, word32 saltSz, int itt, WC_RNG* rng,
        void* heap)
{
#ifdef WOLFSSL_SMALL_STACK
    byte* saltTmp = NULL;
#else
    byte saltTmp[MAX_SALT_SIZE];
#endif
    int genSalt = 0;
    int ret = 0;
    int version = 0;
    int pbeId = 0;
    int blockSz = 0;
    const byte* encOid = NULL;
    int encOidSz = 0;
    word32 padSz = 0;
    word32 innerLen = 0;
    word32 outerLen = 0;
    const byte* pbeOidBuf = NULL;
    word32 pbeOidBufSz = 0;
    word32 pbeLen = 0;
    word32 kdfLen = 0;
    word32 encLen = 0;
    byte cbcIv[MAX_IV_SIZE];
    word32 idx = 0;
    word32 encIdx = 0;

    (void)heap;

    WOLFSSL_ENTER("wc_EncryptPKCS8Key");

    if (key == NULL || outSz == NULL || password == NULL) {
        ret = BAD_FUNC_ARG;
    }

    if (ret == 0) {
        ret = CheckAlgo(vPKCS, pbeOid, &pbeId, &version, &blockSz);
    }
    if (ret == 0 && (salt == NULL || saltSz == 0)) {
        genSalt = 1;
        saltSz = 8;
    }
    if (ret == 0 && version == PKCS5v2) {
        ret = GetAlgoV2(encAlgId, &encOid, &encOidSz, &pbeId, &blockSz);
    }
    if (ret == 0) {
        padSz = (blockSz - (keySz & (blockSz - 1))) & (blockSz - 1);
        /* inner = OCT salt INT itt */
        innerLen = 2 + saltSz + 2 + (itt < 256 ? 1 : 2);

        if (version != PKCS5v2) {
            pbeOidBuf = OidFromId(pbeId, oidPBEType, &pbeOidBufSz);
            /* pbe = OBJ pbse1 SEQ [ inner ] */
            pbeLen = 2 + pbeOidBufSz + 2 + innerLen;
        }
        else {
            pbeOidBuf = pbes2;
            pbeOidBufSz = sizeof(pbes2);
            /* kdf = OBJ pbkdf2 [ SEQ innerLen ] */
            kdfLen = 2 + sizeof(pbkdf2Oid) + 2 + innerLen;
            /* enc = OBJ enc_alg OCT iv */
            encLen = 2 + encOidSz + 2 + blockSz;
            /* pbe = OBJ pbse2 SEQ [ SEQ [ kdf ] SEQ [ enc ] ] */
            pbeLen = 2 + sizeof(pbes2) + 2 + 2 + kdfLen + 2 + encLen;

            ret = wc_RNG_GenerateBlock(rng, cbcIv, blockSz);
        }
    }
    if (ret == 0) {
        /* outerLen = length of PBE encoding + octet string data */
        /* Plus 2 for tag and length for pbe */
        outerLen = 2 + pbeLen;
        /* Octet string tag, length */
        outerLen += 1 + SetLength(keySz + padSz, NULL); 
        /* Octet string bytes */
        outerLen += keySz + padSz;
        if (out == NULL) {
            /* Sequence tag, length */
            *outSz = 1 + SetLength(outerLen, NULL) + outerLen;
            return LENGTH_ONLY_E;
        }
        SetOctetString(keySz + padSz, out);

        idx += SetSequence(outerLen, out + idx);

        encIdx = idx + outerLen - keySz - padSz;
        /* Put Encrypted content in place. */
        XMEMCPY(out + encIdx, key, keySz);
        if (padSz > 0) {
            XMEMSET(out + encIdx + keySz, padSz, padSz);
            keySz += padSz;
        }

        if (genSalt == 1) {
        #ifdef WOLFSSL_SMALL_STACK
            saltTmp = (byte*)XMALLOC(saltSz, heap, DYNAMIC_TYPE_TMP_BUFFER);
            if (saltTmp == NULL) {
                ret = MEMORY_E;
            }
            else
        #endif
            {
                salt = saltTmp;
                if ((ret = wc_RNG_GenerateBlock(rng, saltTmp, saltSz)) != 0) {
                    WOLFSSL_MSG("Error generating random salt");
                }
            }
        }
    }
    if (ret == 0) {
        ret = wc_CryptKey(password, passwordSz, salt, saltSz, itt, pbeId,
                  out + encIdx, keySz, version, cbcIv, 1, 0);
    }
    if (ret == 0) {
        if (version != PKCS5v2) {
            /* PBE algorithm */
            idx += SetSequence(pbeLen, out + idx);
            idx += SetObjectId(pbeOidBufSz, out + idx);
            XMEMCPY(out + idx, pbeOidBuf, pbeOidBufSz);
            idx += pbeOidBufSz;
        }
        else {
            /* PBES2 algorithm identifier */
            idx += SetSequence(pbeLen, out + idx);
            idx += SetObjectId(pbeOidBufSz, out + idx);
            XMEMCPY(out + idx, pbeOidBuf, pbeOidBufSz);
            idx += pbeOidBufSz;
            /* PBES2 Parameters: SEQ [ kdf ] SEQ [ enc ] */
            idx += SetSequence(2 + kdfLen + 2 + encLen, out + idx);
            /* KDF Algorithm Identifier */
            idx += SetSequence(kdfLen, out + idx);
            idx += SetObjectId(sizeof(pbkdf2Oid), out + idx);
            XMEMCPY(out + idx, pbkdf2Oid, sizeof(pbkdf2Oid));
            idx += sizeof(pbkdf2Oid);
        }
        idx += SetSequence(innerLen, out + idx);
        idx += SetOctetString(saltSz, out + idx);
        XMEMCPY(out + idx, salt, saltSz); idx += saltSz;
        ret = SetShortInt(out, &idx, itt, *outSz);
        if (ret > 0)
            ret = 0;
    }
    if (ret == 0) {
        if (version == PKCS5v2) {
            /* Encryption Algorithm Identifier */
            idx += SetSequence(encLen, out + idx);
            idx += SetObjectId(encOidSz, out + idx);
            XMEMCPY(out + idx, encOid, encOidSz);
            idx += encOidSz;
            /* Encryption Algorithm Parameter: CBC IV */
            idx += SetOctetString(blockSz, out + idx);
            XMEMCPY(out + idx, cbcIv, blockSz);
            idx += blockSz;
        }
        idx += SetOctetString(keySz, out + idx);
        /* Default PRF - no need to write out OID */
        idx += keySz;

        ret = idx;
    }

#ifdef WOLFSSL_SMALL_STACK
    if (saltTmp != NULL) {
        XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
    }
#endif

    WOLFSSL_LEAVE("wc_EncryptPKCS8Key", ret);

    return ret;
}
int wc_EncryptPKCS8Key(byte* key, word32 keySz, byte* out, word32* outSz,
        const char* password, int passwordSz, int vPKCS, int pbeOid,
        int encAlgId, byte* salt, word32 saltSz, int itt, WC_RNG* rng,
        void* heap)
{
#ifdef WOLFSSL_SMALL_STACK
    byte* saltTmp = NULL;
#else
    byte saltTmp[MAX_SALT_SIZE];
#endif
    int genSalt = 0;
    int ret = 0;
    int version = 0;
    int pbeId = 0;
    int blockSz = 0;
    const byte* encOid = NULL;
    int encOidSz = 0;
    word32 padSz = 0;
    word32 innerLen = 0;
    word32 outerLen = 0;
    const byte* pbeOidBuf = NULL;
    word32 pbeOidBufSz = 0;
    word32 pbeLen = 0;
    word32 kdfLen = 0;
    word32 encLen = 0;
    byte cbcIv[MAX_IV_SIZE];
    word32 idx = 0;
    word32 encIdx = 0;

    (void)heap;

    WOLFSSL_ENTER("wc_EncryptPKCS8Key");

    if (key == NULL || outSz == NULL || password == NULL) {
        ret = BAD_FUNC_ARG;
    }

    if (ret == 0) {
        ret = CheckAlgo(vPKCS, pbeOid, &pbeId, &version, &blockSz);
    }
    if (ret == 0 && (salt == NULL || saltSz == 0)) {
        genSalt = 1;
        saltSz = 8;
    }
    if (ret == 0 && version == PKCS5v2) {
        ret = GetAlgoV2(encAlgId, &encOid, &encOidSz, &pbeId, &blockSz);
    }
    if (ret == 0) {
        padSz = (blockSz - (keySz & (blockSz - 1))) & (blockSz - 1);
         
        innerLen = 2 + saltSz + 2 + (itt < 256 ? 1 : 2);

        if (version != PKCS5v2) {
            pbeOidBuf = OidFromId(pbeId, oidPBEType, &pbeOidBufSz);
             
            pbeLen = 2 + pbeOidBufSz + 2 + innerLen;
        }
        else {
            pbeOidBuf = pbes2;
            pbeOidBufSz = sizeof(pbes2);
             
            kdfLen = 2 + sizeof(pbkdf2Oid) + 2 + innerLen;
             
            encLen = 2 + encOidSz + 2 + blockSz;
             
            pbeLen = 2 + sizeof(pbes2) + 2 + 2 + kdfLen + 2 + encLen;

            ret = wc_RNG_GenerateBlock(rng, cbcIv, blockSz);
        }
    }
    if (ret == 0) {
         
         
        outerLen = 2 + pbeLen;
         
        outerLen += 1 + SetLength(keySz + padSz, NULL); 
         
        outerLen += keySz + padSz;
        if (out == NULL) {
             
            *outSz = 1 + SetLength(outerLen, NULL) + outerLen;
            return LENGTH_ONLY_E;
        }
        SetOctetString(keySz + padSz, out);

        idx += SetSequence(outerLen, out + idx);

        encIdx = idx + outerLen - keySz - padSz;
         
        XMEMCPY(out + encIdx, key, keySz);
        if (padSz > 0) {
            XMEMSET(out + encIdx + keySz, padSz, padSz);
            keySz += padSz;
        }

        if (genSalt == 1) {
        #ifdef WOLFSSL_SMALL_STACK
            saltTmp = (byte*)XMALLOC(saltSz, heap, DYNAMIC_TYPE_TMP_BUFFER);
            if (saltTmp == NULL) {
                ret = MEMORY_E;
            }
            else
        #endif
            {
                salt = saltTmp;
                if ((ret = wc_RNG_GenerateBlock(rng, saltTmp, saltSz)) != 0) {
                    WOLFSSL_MSG("Error generating random salt");
                }
            }
        }
    }
    if (ret == 0) {
        ret = wc_CryptKey(password, passwordSz, salt, saltSz, itt, pbeId,
                  out + encIdx, keySz, version, cbcIv, 1, 0);
    }
    if (ret == 0) {
        if (version != PKCS5v2) {
             
            idx += SetSequence(pbeLen, out + idx);
            idx += SetObjectId(pbeOidBufSz, out + idx);
            XMEMCPY(out + idx, pbeOidBuf, pbeOidBufSz);
            idx += pbeOidBufSz;
        }
        else {
             
            idx += SetSequence(pbeLen, out + idx);
            idx += SetObjectId(pbeOidBufSz, out + idx);
            XMEMCPY(out + idx, pbeOidBuf, pbeOidBufSz);
            idx += pbeOidBufSz;
             
            idx += SetSequence(2 + kdfLen + 2 + encLen, out + idx);
             
            idx += SetSequence(kdfLen, out + idx);
            idx += SetObjectId(sizeof(pbkdf2Oid), out + idx);
            XMEMCPY(out + idx, pbkdf2Oid, sizeof(pbkdf2Oid));
            idx += sizeof(pbkdf2Oid);
        }
        idx += SetSequence(innerLen, out + idx);
        idx += SetOctetString(saltSz, out + idx);
        XMEMCPY(out + idx, salt, saltSz); idx += saltSz;
        ret = SetShortInt(out, &idx, itt, *outSz);
        if (ret > 0)
            ret = 0;
    }
    if (ret == 0) {
        if (version == PKCS5v2) {
             
            idx += SetSequence(encLen, out + idx);
            idx += SetObjectId(encOidSz, out + idx);
            XMEMCPY(out + idx, encOid, encOidSz);
            idx += encOidSz;
             
            idx += SetOctetString(blockSz, out + idx);
            XMEMCPY(out + idx, cbcIv, blockSz);
            idx += blockSz;
        }
        idx += SetOctetString(keySz, out + idx);
         
        idx += keySz;

        ret = idx;
    }

#ifdef WOLFSSL_SMALL_STACK
    if (saltTmp != NULL) {
        XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
    }
#endif

    WOLFSSL_LEAVE("wc_EncryptPKCS8Key", ret);

    return ret;
}