summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/asn1/asn1-basics.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/asn1/asn1-basics.cpp')
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-basics.cpp611
1 files changed, 611 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/asn1/asn1-basics.cpp b/src/VBox/Runtime/common/asn1/asn1-basics.cpp
new file mode 100644
index 00000000..05c73f1a
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-basics.cpp
@@ -0,0 +1,611 @@
+/* $Id: asn1-basics.cpp $ */
+/** @file
+ * IPRT - ASN.1, Basic Operations.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/asn1.h>
+
+#include <iprt/alloca.h>
+#include <iprt/bignum.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/uni.h>
+
+#include <iprt/formats/asn1.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * ASN.1 content/value allocation.
+ *
+ * The currently most frequent use of the RTAsn1 module is to decode ASN.1 byte
+ * streams. In that scenario we do not allocate memory for the raw content
+ * bytes, but share it with the byte stream. Also, a great number of RTASN1CORE
+ * structures will never need to have any content bytes allocated with this.
+ *
+ * So, in order to avoid adding an extra 16 (64-bit) or 8 (32-bit) bytes to each
+ * RTASN1CORE structure just to keep track of the occational content allocation,
+ * we put the allocator tracking structure inside the allocation. During
+ * allocator operations it lives temporarily on the stack.
+ */
+typedef struct RTASN1MEMCONTENT
+{
+ /** The allocation tracker. */
+ RTASN1ALLOCATION Allocation;
+#if ARCH_BITS == 32
+ uint32_t Padding; /**< Alignment padding. */
+#endif
+ /** The content bytes, i.e. what RTASN1CORE::uData.pv points to. Use a 64-bit
+ * type here to emphasize that it's 8-byte aligned on all platforms. */
+ uint64_t au64Content[1];
+} RTASN1MEMCONTENT;
+AssertCompileMemberAlignment(RTASN1MEMCONTENT, au64Content, 8);
+/** Pointer to a ASN.1 content allocation. */
+typedef RTASN1MEMCONTENT *PRTASN1MEMCONTENT;
+
+
+
+RTDECL(int) RTAsn1MemResizeArray(PRTASN1ARRAYALLOCATION pAllocation, void ***ppapvArray, uint32_t cCurrent, uint32_t cNew)
+{
+ AssertReturn(pAllocation->pAllocator != NULL, VERR_WRONG_ORDER);
+ AssertReturn(pAllocation->cbEntry > 0, VERR_WRONG_ORDER);
+ AssertReturn(cCurrent <= pAllocation->cEntriesAllocated, VERR_INVALID_PARAMETER);
+ AssertReturn(cCurrent <= pAllocation->cPointersAllocated, VERR_INVALID_PARAMETER);
+ AssertReturn(cNew < _1M, VERR_OUT_OF_RANGE);
+ Assert(pAllocation->cEntriesAllocated <= pAllocation->cPointersAllocated);
+
+ /*
+ * Is there sufficent space allocated already?
+ *
+ * We keep unused entires ZEROed, therefore we must always call the allocator
+ * when shrinking (this also helps with the electric fence allocator).
+ */
+ if (cNew <= pAllocation->cEntriesAllocated)
+ {
+ if (cCurrent <= cNew)
+ return VINF_SUCCESS;
+ pAllocation->pAllocator->pfnShrinkArray(pAllocation->pAllocator, pAllocation, ppapvArray, cCurrent, cNew);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Must grow (or do initial alloc).
+ */
+ pAllocation->cResizeCalls++;
+ return pAllocation->pAllocator->pfnGrowArray(pAllocation->pAllocator, pAllocation, ppapvArray, cNew);
+}
+
+
+RTDECL(void) RTAsn1MemFreeArray(PRTASN1ARRAYALLOCATION pAllocation, void **papvArray)
+{
+ Assert(pAllocation->pAllocator != NULL);
+ if (papvArray)
+ {
+ pAllocation->pAllocator->pfnFreeArray(pAllocation->pAllocator, pAllocation, papvArray);
+ Assert(pAllocation->cPointersAllocated == 0);
+ Assert(pAllocation->cEntriesAllocated == 0);
+ }
+}
+
+
+RTDECL(int) RTAsn1MemAllocZ(PRTASN1ALLOCATION pAllocation, void **ppvMem, size_t cbMem)
+{
+ AssertReturn(pAllocation->pAllocator != NULL, VERR_WRONG_ORDER);
+ AssertPtr(ppvMem);
+ Assert(cbMem > 0);
+ int rc = pAllocation->pAllocator->pfnAlloc(pAllocation->pAllocator, pAllocation, ppvMem, cbMem);
+ Assert(pAllocation->cbAllocated >= cbMem || RT_FAILURE_NP(rc));
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1MemDup(PRTASN1ALLOCATION pAllocation, void **ppvMem, const void *pvSrc, size_t cbMem)
+{
+ AssertReturn(pAllocation->pAllocator != NULL, VERR_WRONG_ORDER);
+ AssertPtr(ppvMem);
+ AssertPtr(pvSrc);
+ Assert(cbMem > 0);
+ int rc = pAllocation->pAllocator->pfnAlloc(pAllocation->pAllocator, pAllocation, ppvMem, cbMem);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(pAllocation->cbAllocated >= cbMem);
+ memcpy(*ppvMem, pvSrc, cbMem);
+ return VINF_SUCCESS;
+ }
+ return rc;
+}
+
+
+RTDECL(void) RTAsn1MemFree(PRTASN1ALLOCATION pAllocation, void *pv)
+{
+ Assert(pAllocation->pAllocator != NULL);
+ if (pv)
+ {
+ pAllocation->pAllocator->pfnFree(pAllocation->pAllocator, pAllocation, pv);
+ Assert(pAllocation->cbAllocated == 0);
+ }
+}
+
+
+RTDECL(PRTASN1ALLOCATION) RTAsn1MemInitAllocation(PRTASN1ALLOCATION pAllocation, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ pAllocation->cbAllocated = 0;
+ pAllocation->cReallocs = 0;
+ pAllocation->uReserved0 = 0;
+ pAllocation->pAllocator = pAllocator;
+ return pAllocation;
+}
+
+
+RTDECL(PRTASN1ARRAYALLOCATION) RTAsn1MemInitArrayAllocation(PRTASN1ARRAYALLOCATION pAllocation,
+ PCRTASN1ALLOCATORVTABLE pAllocator, size_t cbEntry)
+{
+ Assert(cbEntry >= sizeof(RTASN1CORE));
+ Assert(cbEntry < _1M);
+ Assert(RT_ALIGN_Z(cbEntry, sizeof(void *)) == cbEntry);
+ pAllocation->cbEntry = (uint32_t)cbEntry;
+ pAllocation->cPointersAllocated = 0;
+ pAllocation->cEntriesAllocated = 0;
+ pAllocation->cResizeCalls = 0;
+ pAllocation->uReserved0 = 0;
+ pAllocation->pAllocator = pAllocator;
+ return pAllocation;
+}
+
+
+RTDECL(int) RTAsn1ContentAllocZ(PRTASN1CORE pAsn1Core, size_t cb, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ AssertReturn(pAllocator != NULL, VERR_WRONG_ORDER);
+ AssertReturn(cb > 0 && cb < _1G, VERR_INVALID_PARAMETER);
+ AssertPtr(pAsn1Core);
+ AssertReturn(!(pAsn1Core->fFlags & RTASN1CORE_F_ALLOCATED_CONTENT), VERR_INVALID_STATE);
+
+ /* Initialize the temporary allocation tracker. */
+ RTASN1ALLOCATION Allocation;
+ Allocation.cbAllocated = 0;
+ Allocation.cReallocs = 0;
+ Allocation.uReserved0 = 0;
+ Allocation.pAllocator = pAllocator;
+
+ /* Make the allocation. */
+ uint32_t cbAlloc = RT_UOFFSETOF(RTASN1MEMCONTENT, au64Content) + (uint32_t)cb;
+ PRTASN1MEMCONTENT pHdr;
+ int rc = pAllocator->pfnAlloc(pAllocator, &Allocation, (void **)&pHdr, cbAlloc);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(Allocation.cbAllocated >= cbAlloc);
+ pHdr->Allocation = Allocation;
+ pAsn1Core->cb = (uint32_t)cb;
+ pAsn1Core->uData.pv = &pHdr->au64Content[0];
+ pAsn1Core->fFlags |= RTASN1CORE_F_ALLOCATED_CONTENT;
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1ContentDup(PRTASN1CORE pAsn1Core, void const *pvSrc, size_t cbSrc, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ int rc = RTAsn1ContentAllocZ(pAsn1Core, cbSrc, pAllocator);
+ if (RT_SUCCESS(rc))
+ memcpy((void *)pAsn1Core->uData.pv, pvSrc, cbSrc);
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1ContentReallocZ(PRTASN1CORE pAsn1Core, size_t cb, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ /* Validate input. */
+ AssertPtr(pAsn1Core);
+ AssertReturn(cb < _1G, VERR_INVALID_PARAMETER);
+
+ if (cb > 0)
+ {
+ /*
+ * Case 1 - Initial allocation.
+ */
+ uint32_t cbNeeded = RT_UOFFSETOF(RTASN1MEMCONTENT, au64Content) + (uint32_t)cb;
+ if (!(pAsn1Core->fFlags & RTASN1CORE_F_ALLOCATED_CONTENT))
+ return RTAsn1ContentAllocZ(pAsn1Core, cb, pAllocator);
+
+ /* Locate the header. */
+ PRTASN1MEMCONTENT pHdr = RT_FROM_MEMBER(pAsn1Core->uData.pv, RTASN1MEMCONTENT, au64Content);
+
+ /*
+ * Case 2 - Reallocation using the same allocator.
+ */
+ if ( pHdr->Allocation.pAllocator == pAllocator
+ || !pAllocator)
+ {
+ pHdr->Allocation.cReallocs++;
+
+ /* Modify the allocation if necessary. */
+ if (pHdr->Allocation.cbAllocated < cbNeeded)
+ {
+ RTASN1ALLOCATION Allocation = pHdr->Allocation;
+ int rc = Allocation.pAllocator->pfnRealloc(Allocation.pAllocator, &Allocation, pHdr, (void **)&pHdr, cbNeeded);
+ if (RT_FAILURE(rc))
+ return rc;
+ Assert(Allocation.cbAllocated >= cbNeeded);
+ pAsn1Core->uData.pv = &pHdr->au64Content[0];
+ pHdr->Allocation = Allocation;
+ }
+
+ /* Clear any additional memory we're letting the user use and
+ update the content size. */
+ if (pAsn1Core->cb < cb)
+ RT_BZERO((uint8_t *)&pAsn1Core->uData.pu8[pAsn1Core->cb], cb - pAsn1Core->cb);
+ pAsn1Core->cb = (uint32_t)cb;
+ }
+ /*
+ * Case 3 - Reallocation using a different allocator.
+ */
+ else
+ {
+ /* Initialize the temporary allocation tracker. */
+ RTASN1ALLOCATION Allocation;
+ Allocation.cbAllocated = 0;
+ Allocation.cReallocs = pHdr->Allocation.cReallocs + 1;
+ Allocation.uReserved0 = 0;
+ Allocation.pAllocator = pAllocator;
+
+ /* Make the allocation. */
+ PRTASN1MEMCONTENT pHdrNew;
+ int rc = pAllocator->pfnAlloc(pAllocator, &Allocation, (void **)&pHdrNew, cbNeeded);
+ if (RT_FAILURE(rc))
+ return rc;
+ Assert(Allocation.cbAllocated >= cbNeeded);
+
+ /* Duplicate the old content and zero any new memory we might've added. */
+ if (pAsn1Core->cb >= cb)
+ memcpy(&pHdrNew->au64Content[0], &pHdr->au64Content[0], cb);
+ else
+ {
+ memcpy(&pHdrNew->au64Content[0], &pHdr->au64Content[0], pAsn1Core->cb);
+ RT_BZERO((uint8_t *)&pHdrNew->au64Content[0] + pAsn1Core->cb, cb - pAsn1Core->cb);
+ }
+
+ /* Update the core. */
+ pHdrNew->Allocation = Allocation;
+ pAsn1Core->uData.pv = &pHdrNew->au64Content[0];
+ pAsn1Core->fFlags |= RTASN1CORE_F_ALLOCATED_CONTENT; /* free cleared it. */
+ pAsn1Core->cb = (uint32_t)cb;
+
+ /* Free the old content. */
+ Allocation = pHdr->Allocation;
+ Allocation.pAllocator->pfnFree(Allocation.pAllocator, &Allocation, pHdr);
+ Assert(Allocation.cbAllocated == 0);
+ }
+ }
+ /*
+ * Case 4 - It's a request to free the memory.
+ */
+ else
+ RTAsn1ContentFree(pAsn1Core);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(void) RTAsn1ContentFree(PRTASN1CORE pAsn1Core)
+{
+ if (pAsn1Core)
+ {
+ pAsn1Core->cb = 0;
+ if (pAsn1Core->fFlags & RTASN1CORE_F_ALLOCATED_CONTENT)
+ {
+ pAsn1Core->fFlags &= ~RTASN1CORE_F_ALLOCATED_CONTENT;
+ AssertReturnVoid(pAsn1Core->uData.pv);
+
+ PRTASN1MEMCONTENT pHdr = RT_FROM_MEMBER(pAsn1Core->uData.pv, RTASN1MEMCONTENT, au64Content);
+ RTASN1ALLOCATION Allocation = pHdr->Allocation;
+
+ Allocation.pAllocator->pfnFree(Allocation.pAllocator, &Allocation, pHdr);
+ Assert(Allocation.cbAllocated == 0);
+ }
+ pAsn1Core->uData.pv = NULL;
+ }
+}
+
+
+
+/*
+ * Virtual method table based API.
+ */
+
+RTDECL(void) RTAsn1VtDelete(PRTASN1CORE pThisCore)
+{
+ if (pThisCore)
+ {
+ PCRTASN1COREVTABLE pOps = pThisCore->pOps;
+ if (pOps)
+ pOps->pfnDtor(pThisCore);
+ }
+}
+
+
+/**
+ * Context data passed by RTAsn1VtDeepEnum to it's worker callbacks.
+ */
+typedef struct RTASN1DEEPENUMCTX
+{
+ PFNRTASN1ENUMCALLBACK pfnCallback;
+ void *pvUser;
+} RTASN1DEEPENUMCTX;
+
+
+static DECLCALLBACK(int) rtAsn1VtDeepEnumDepthFirst(PRTASN1CORE pThisCore, const char *pszName, uint32_t uDepth, void *pvUser)
+{
+ AssertReturn(pThisCore, VINF_SUCCESS);
+
+ if (pThisCore->pOps && pThisCore->pOps->pfnEnum)
+ {
+ int rc = pThisCore->pOps->pfnEnum(pThisCore, rtAsn1VtDeepEnumDepthFirst, uDepth, pvUser);
+ if (rc != VINF_SUCCESS)
+ return rc;
+ }
+
+ RTASN1DEEPENUMCTX *pCtx = (RTASN1DEEPENUMCTX *)pvUser;
+ return pCtx->pfnCallback(pThisCore, pszName, uDepth, pCtx->pvUser);
+}
+
+
+static DECLCALLBACK(int) rtAsn1VtDeepEnumDepthLast(PRTASN1CORE pThisCore, const char *pszName, uint32_t uDepth, void *pvUser)
+{
+ AssertReturn(pThisCore, VINF_SUCCESS);
+
+ RTASN1DEEPENUMCTX *pCtx = (RTASN1DEEPENUMCTX *)pvUser;
+ int rc = pCtx->pfnCallback(pThisCore, pszName, uDepth, pCtx->pvUser);
+ if (rc == VINF_SUCCESS)
+ {
+ if (pThisCore->pOps && pThisCore->pOps->pfnEnum)
+ rc = pThisCore->pOps->pfnEnum(pThisCore, rtAsn1VtDeepEnumDepthFirst, uDepth, pvUser);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1VtDeepEnum(PRTASN1CORE pThisCore, bool fDepthFirst, uint32_t uDepth,
+ PFNRTASN1ENUMCALLBACK pfnCallback, void *pvUser)
+{
+ int rc;
+ if (RTAsn1Core_IsPresent(pThisCore))
+ {
+ PCRTASN1COREVTABLE pOps = pThisCore->pOps;
+ if (pOps && pOps->pfnEnum)
+ {
+ RTASN1DEEPENUMCTX Ctx;
+ Ctx.pfnCallback = pfnCallback;
+ Ctx.pvUser = pvUser;
+ rc = pOps->pfnEnum(pThisCore, fDepthFirst ? rtAsn1VtDeepEnumDepthFirst : rtAsn1VtDeepEnumDepthLast, uDepth, &Ctx);
+ }
+ else
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VINF_SUCCESS;
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1VtClone(PRTASN1CORE pThisCore, PRTASN1CORE pSrcCore, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ AssertPtrReturn(pThisCore, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSrcCore, VERR_INVALID_POINTER);
+ AssertPtrReturn(pAllocator, VERR_INVALID_POINTER);
+
+ if (RTAsn1Core_IsPresent(pSrcCore))
+ {
+ AssertPtrReturn(pSrcCore->pOps, VERR_INVALID_POINTER);
+ AssertPtr(pSrcCore->pOps->pfnClone);
+ return pSrcCore->pOps->pfnClone(pThisCore, pSrcCore, pAllocator);
+ }
+
+ RT_ZERO(*pThisCore);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1VtCompare(PCRTASN1CORE pLeftCore, PCRTASN1CORE pRightCore)
+{
+ int iDiff;
+ if (RTAsn1Core_IsPresent(pLeftCore))
+ {
+ if (RTAsn1Core_IsPresent(pRightCore))
+ {
+ PCRTASN1COREVTABLE pOps = pLeftCore->pOps;
+ if (pOps == pRightCore->pOps)
+ {
+ AssertPtr(pOps->pfnCompare);
+ iDiff = pOps->pfnCompare(pLeftCore, pRightCore);
+ }
+ else
+ iDiff = (uintptr_t)pOps < (uintptr_t)pRightCore->pOps ? -1 : 1;
+ }
+ else
+ iDiff = 1;
+ }
+ else
+ iDiff = 0 - (int)RTAsn1Core_IsPresent(pRightCore);
+ return iDiff;
+}
+
+
+RTDECL(int) RTAsn1VtCheckSanity(PCRTASN1CORE pThisCore, uint32_t fFlags,
+ PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ int rc;
+ if (RTAsn1Core_IsPresent(pThisCore))
+ {
+ PCRTASN1COREVTABLE pOps = pThisCore->pOps;
+ if (pOps && pOps->pfnCheckSanity)
+ rc = pOps->pfnCheckSanity(pThisCore, fFlags, pErrInfo, pszErrorTag);
+ else if (pOps)
+ rc = RTErrInfoSetF(pErrInfo, VERR_ASN1_NO_CHECK_SANITY_METHOD,
+ "%s: Has no pfnCheckSanity function.", pszErrorTag);
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_ASN1_NO_VTABLE, "%s: Has no Vtable function.", pszErrorTag);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_ASN1_NOT_PRESENT, "%s: Not present.", pszErrorTag);
+ return rc;
+}
+
+
+
+/*
+ * Dummy ASN.1 object.
+ */
+
+RTDECL(int) RTAsn1Dummy_InitEx(PRTASN1DUMMY pThis)
+{
+ return RTAsn1Core_InitEx(&pThis->Asn1Core,
+ UINT32_MAX,
+ ASN1_TAGCLASS_PRIVATE | ASN1_TAGFLAG_CONSTRUCTED,
+ NULL,
+ RTASN1CORE_F_DUMMY);
+}
+
+
+/*
+ * ASN.1 SEQUENCE OF object.
+ */
+
+RTDECL(int) RTAsn1SeqOfCore_Init(PRTASN1SEQOFCORE pThis, PCRTASN1COREVTABLE pVtable)
+{
+ return RTAsn1Core_InitEx(&pThis->Asn1Core,
+ ASN1_TAG_SEQUENCE,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_CONSTRUCTED,
+ pVtable,
+ RTASN1CORE_F_PRESENT);
+}
+
+
+RTDECL(int) RTAsn1SeqOfCore_Clone(PRTASN1SEQOFCORE pThis, PCRTASN1COREVTABLE pVtable, PCRTASN1SEQOFCORE pSrc)
+{
+ AssertReturn(pSrc->Asn1Core.pOps == pVtable, VERR_ASN1_INTERNAL_ERROR_5);
+ return RTAsn1Core_CloneNoContent(&pThis->Asn1Core, &pSrc->Asn1Core);
+}
+
+
+/*
+ * ASN.1 SET OF object.
+ */
+
+RTDECL(int) RTAsn1SetOfCore_Init(PRTASN1SETOFCORE pThis, PCRTASN1COREVTABLE pVtable)
+{
+ return RTAsn1Core_InitEx(&pThis->Asn1Core,
+ ASN1_TAG_SET,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_CONSTRUCTED,
+ pVtable,
+ RTASN1CORE_F_PRESENT);
+}
+
+
+RTDECL(int) RTAsn1SetOfCore_Clone(PRTASN1SETOFCORE pThis, PCRTASN1COREVTABLE pVtable, PCRTASN1SETOFCORE pSrc)
+{
+ AssertReturn(pSrc->Asn1Core.pOps == pVtable, VERR_ASN1_INTERNAL_ERROR_5);
+ return RTAsn1Core_CloneNoContent(&pThis->Asn1Core, &pSrc->Asn1Core);
+}
+
+
+/*
+ * ASN.1 SEQUENCE object.
+ */
+
+RTDECL(int) RTAsn1SequenceCore_Init(PRTASN1SEQUENCECORE pThis, PCRTASN1COREVTABLE pVtable)
+{
+ return RTAsn1Core_InitEx(&pThis->Asn1Core,
+ ASN1_TAG_SEQUENCE,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_CONSTRUCTED,
+ pVtable,
+ RTASN1CORE_F_PRESENT);
+}
+
+
+RTDECL(int) RTAsn1SequenceCore_Clone(PRTASN1SEQUENCECORE pThis, PCRTASN1COREVTABLE pVtable, PCRTASN1SEQUENCECORE pSrc)
+{
+ AssertReturn(pSrc->Asn1Core.pOps == pVtable, VERR_ASN1_INTERNAL_ERROR_5);
+ return RTAsn1Core_CloneNoContent(&pThis->Asn1Core, &pSrc->Asn1Core);
+}
+
+
+/*
+ * ASN.1 SEQUENCE object - only used by SPC, so probably doing something wrong there.
+ */
+
+RTDECL(int) RTAsn1SetCore_Init(PRTASN1SETCORE pThis, PCRTASN1COREVTABLE pVtable)
+{
+ return RTAsn1Core_InitEx(&pThis->Asn1Core,
+ ASN1_TAG_SET,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_CONSTRUCTED,
+ pVtable,
+ RTASN1CORE_F_PRESENT);
+}
+
+
+RTDECL(int) RTAsn1SetCore_Clone(PRTASN1SETCORE pThis, PCRTASN1COREVTABLE pVtable, PCRTASN1SETCORE pSrc)
+{
+ AssertReturn(pSrc->Asn1Core.pOps == pVtable, VERR_ASN1_INTERNAL_ERROR_5);
+ return RTAsn1Core_CloneNoContent(&pThis->Asn1Core, &pSrc->Asn1Core);
+}
+
+
+/*
+ * ASN.1 Context Tag object.
+ */
+
+RTDECL(int) RTAsn1ContextTagN_Init(PRTASN1CONTEXTTAG pThis, uint32_t uTag, PCRTASN1COREVTABLE pVtable)
+{
+ return RTAsn1Core_InitEx(&pThis->Asn1Core,
+ uTag,
+ ASN1_TAGCLASS_CONTEXT | ASN1_TAGFLAG_CONSTRUCTED,
+ pVtable,
+ RTASN1CORE_F_PRESENT);
+}
+
+
+RTDECL(int) RTAsn1ContextTagN_Clone(PRTASN1CONTEXTTAG pThis, PCRTASN1CONTEXTTAG pSrc, uint32_t uTag)
+{
+ Assert(pSrc->Asn1Core.uTag == uTag || !RTASN1CORE_IS_PRESENT(&pSrc->Asn1Core)); RT_NOREF_PV(uTag);
+ return RTAsn1Core_CloneNoContent(&pThis->Asn1Core, &pSrc->Asn1Core);
+}
+