summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/asn1
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Runtime/common/asn1
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Runtime/common/asn1')
-rw-r--r--src/VBox/Runtime/common/asn1/Makefile.kup0
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-basics.cpp611
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-cursor.cpp678
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-default-allocator.cpp231
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-dump.cpp644
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-efence-allocator.cpp215
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-encode.cpp535
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-safer-allocator.cpp232
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-bitstring-decode.cpp141
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-bitstring-template.h55
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-bitstring.cpp548
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-boolean-decode.cpp93
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-boolean-template.h55
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-boolean.cpp218
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-core-decode.cpp69
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-core-template.h55
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-core.cpp331
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-dyntype-decode.cpp250
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-dyntype.cpp197
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-integer-decode.cpp97
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-integer-template.h55
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-integer.cpp508
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-null-decode.cpp73
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-null.cpp141
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-objid-decode.cpp377
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-objid-template.h64
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-objid.cpp566
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-octetstring-decode.cpp90
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-octetstring-template.h55
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-octetstring.cpp459
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-string-decode.cpp199
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-string-template.h55
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-string-template2.h49
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-string.cpp1855
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-time-decode.cpp415
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-time-template.h55
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-time-template2.h40
-rw-r--r--src/VBox/Runtime/common/asn1/asn1-ut-time.cpp359
-rw-r--r--src/VBox/Runtime/common/asn1/oiddb.cfg351
-rw-r--r--src/VBox/Runtime/common/asn1/oiddb2c.cpp635
40 files changed, 11656 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/asn1/Makefile.kup b/src/VBox/Runtime/common/asn1/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/Makefile.kup
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);
+}
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-cursor.cpp b/src/VBox/Runtime/common/asn1/asn1-cursor.cpp
new file mode 100644
index 00000000..3150e67c
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-cursor.cpp
@@ -0,0 +1,678 @@
+/* $Id: asn1-cursor.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/asm.h>
+#include <iprt/alloca.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/ctype.h>
+
+#include <iprt/formats/asn1.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @def RTASN1_MAX_NESTING
+ * The maximum nesting depth we allow. This limit is enforced to avoid running
+ * out of stack due to malformed ASN.1 input.
+ *
+ * For reference, 'RTSignTool verify-exe RTSignTool.exe', requires a value of 15
+ * to work without hitting the limit for signatures with simple timestamps, and
+ * 23 (amd64/rel = ~3KB) for the new microsoft timestamp counter signatures.
+ */
+#ifdef IN_RING3
+# define RTASN1_MAX_NESTING 64
+#else
+# define RTASN1_MAX_NESTING 32
+#endif
+
+
+
+RTDECL(PRTASN1CURSOR) RTAsn1CursorInitPrimary(PRTASN1CURSORPRIMARY pPrimaryCursor, void const *pvFirst, uint32_t cb,
+ PRTERRINFO pErrInfo, PCRTASN1ALLOCATORVTABLE pAllocator, uint32_t fFlags,
+ const char *pszErrorTag)
+{
+ pPrimaryCursor->Cursor.pbCur = (uint8_t const *)pvFirst;
+ pPrimaryCursor->Cursor.cbLeft = cb;
+ pPrimaryCursor->Cursor.fFlags = (uint8_t)fFlags; Assert(fFlags <= UINT8_MAX);
+ pPrimaryCursor->Cursor.cDepth = 0;
+ pPrimaryCursor->Cursor.abReserved[0] = 0;
+ pPrimaryCursor->Cursor.abReserved[1] = 0;
+ pPrimaryCursor->Cursor.pPrimary = pPrimaryCursor;
+ pPrimaryCursor->Cursor.pUp = NULL;
+ pPrimaryCursor->Cursor.pszErrorTag = pszErrorTag;
+ pPrimaryCursor->pErrInfo = pErrInfo;
+ pPrimaryCursor->pAllocator = pAllocator;
+ pPrimaryCursor->pbFirst = (uint8_t const *)pvFirst;
+ return &pPrimaryCursor->Cursor;
+}
+
+
+RTDECL(int) RTAsn1CursorInitSub(PRTASN1CURSOR pParent, uint32_t cb, PRTASN1CURSOR pChild, const char *pszErrorTag)
+{
+ AssertReturn(pParent->pPrimary, VERR_ASN1_INTERNAL_ERROR_1);
+ AssertReturn(pParent->pbCur, VERR_ASN1_INTERNAL_ERROR_2);
+
+ pChild->pbCur = pParent->pbCur;
+ pChild->cbLeft = cb;
+ pChild->fFlags = pParent->fFlags & ~RTASN1CURSOR_FLAGS_INDEFINITE_LENGTH;
+ pChild->cDepth = pParent->cDepth + 1;
+ AssertReturn(pChild->cDepth < RTASN1_MAX_NESTING, VERR_ASN1_TOO_DEEPLY_NESTED);
+ pChild->abReserved[0] = 0;
+ pChild->abReserved[1] = 0;
+ pChild->pPrimary = pParent->pPrimary;
+ pChild->pUp = pParent;
+ pChild->pszErrorTag = pszErrorTag;
+
+ AssertReturn(pParent->cbLeft >= cb, VERR_ASN1_INTERNAL_ERROR_3);
+ pParent->pbCur += cb;
+ pParent->cbLeft -= cb;
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1CursorInitSubFromCore(PRTASN1CURSOR pParent, PRTASN1CORE pAsn1Core,
+ PRTASN1CURSOR pChild, const char *pszErrorTag)
+{
+ AssertReturn(pParent->pPrimary, VERR_ASN1_INTERNAL_ERROR_1);
+ AssertReturn(pParent->pbCur, VERR_ASN1_INTERNAL_ERROR_2);
+
+ pChild->pbCur = pAsn1Core->uData.pu8;
+ pChild->cbLeft = pAsn1Core->cb;
+ pChild->fFlags = pParent->fFlags & ~RTASN1CURSOR_FLAGS_INDEFINITE_LENGTH;
+ pChild->cDepth = pParent->cDepth + 1;
+ AssertReturn(pChild->cDepth < RTASN1_MAX_NESTING, VERR_ASN1_TOO_DEEPLY_NESTED);
+ pChild->abReserved[0] = 0;
+ pChild->abReserved[1] = 0;
+ pChild->pPrimary = pParent->pPrimary;
+ pChild->pUp = pParent;
+ pChild->pszErrorTag = pszErrorTag;
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1CursorSetInfoV(PRTASN1CURSOR pCursor, int rc, const char *pszMsg, va_list va)
+{
+ PRTERRINFO pErrInfo = pCursor->pPrimary->pErrInfo;
+ if (pErrInfo)
+ {
+ /* Format the message. */
+ RTErrInfoSetV(pErrInfo, rc, pszMsg, va);
+
+ /* Add the prefixes. This isn't the fastest way, but it's the one
+ which eats the least stack. */
+ char *pszBuf = pErrInfo->pszMsg;
+ size_t cbBuf = pErrInfo->cbMsg;
+ if (pszBuf && cbBuf > 32)
+ {
+ size_t cbMove = strlen(pszBuf) + 1;
+
+ /* Make sure there is a ': '. */
+ bool fFirst = false;
+ if (pszMsg[0] != '%' || pszMsg[1] != 's' || pszMsg[2] != ':')
+ {
+ if (cbMove + 2 < cbBuf)
+ {
+ memmove(pszBuf + 2, pszBuf, cbMove);
+ pszBuf[0] = ':';
+ pszBuf[1] = ' ';
+ cbMove += 2;
+ fFirst = true;
+ }
+ }
+
+ /* Add the prefixes from the cursor chain. */
+ while (pCursor)
+ {
+ if (pCursor->pszErrorTag)
+ {
+ size_t cchErrorTag = strlen(pCursor->pszErrorTag);
+ if (cchErrorTag + !fFirst + cbMove > cbBuf)
+ break;
+ memmove(pszBuf + cchErrorTag + !fFirst, pszBuf, cbMove);
+ memcpy(pszBuf, pCursor->pszErrorTag, cchErrorTag);
+ if (!fFirst)
+ pszBuf[cchErrorTag] = '.';
+ cbMove += cchErrorTag + !fFirst;
+ fFirst = false;
+ }
+ pCursor = pCursor->pUp;
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1CursorSetInfo(PRTASN1CURSOR pCursor, int rc, const char *pszMsg, ...)
+{
+ va_list va;
+ va_start(va, pszMsg);
+ rc = RTAsn1CursorSetInfoV(pCursor, rc, pszMsg, va);
+ va_end(va);
+ return rc;
+}
+
+
+RTDECL(bool) RTAsn1CursorIsEnd(PRTASN1CURSOR pCursor)
+{
+ if (pCursor->cbLeft == 0)
+ return true;
+ if (!(pCursor->fFlags & RTASN1CURSOR_FLAGS_INDEFINITE_LENGTH))
+ return false;
+ return pCursor->cbLeft >= 2
+ && pCursor->pbCur[0] == 0
+ && pCursor->pbCur[1] == 0;
+}
+
+
+RTDECL(int) RTAsn1CursorCheckEnd(PRTASN1CURSOR pCursor)
+{
+ if (!(pCursor->fFlags & RTASN1CURSOR_FLAGS_INDEFINITE_LENGTH))
+ {
+ if (pCursor->cbLeft == 0)
+ return VINF_SUCCESS;
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_NOT_AT_END,
+ "%u (%#x) bytes left over", pCursor->cbLeft, pCursor->cbLeft);
+ }
+
+ /*
+ * There must be exactly two zero bytes here.
+ */
+ if (pCursor->cbLeft == 2)
+ {
+ if ( pCursor->pbCur[0] == 0
+ && pCursor->pbCur[1] == 0)
+ return VINF_SUCCESS;
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_NOT_AT_END,
+ "%u (%#x) bytes left over [indef: %.*Rhxs]",
+ pCursor->cbLeft, pCursor->cbLeft, RT_MIN(pCursor->cbLeft, 16), pCursor->pbCur);
+ }
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_NOT_AT_END,
+ "%u (%#x) byte(s) left over, exepcted exactly two zero bytes [indef len]",
+ pCursor->cbLeft, pCursor->cbLeft);
+}
+
+
+/**
+ * Worker for RTAsn1CursorCheckSeqEnd and RTAsn1CursorCheckSetEnd.
+ */
+static int rtAsn1CursorCheckSeqOrSetEnd(PRTASN1CURSOR pCursor, PRTASN1CORE pAsn1Core)
+{
+ if (!(pAsn1Core->fFlags & RTASN1CORE_F_INDEFINITE_LENGTH))
+ {
+ if (pCursor->cbLeft == 0)
+ return VINF_SUCCESS;
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_NOT_AT_END,
+ "%u (%#x) bytes left over", pCursor->cbLeft, pCursor->cbLeft);
+ }
+
+ if (pCursor->cbLeft >= 2)
+ {
+ if ( pCursor->pbCur[0] == 0
+ && pCursor->pbCur[1] == 0)
+ {
+ pAsn1Core->cb = (uint32_t)(pCursor->pbCur - pAsn1Core->uData.pu8);
+ pCursor->cbLeft -= 2;
+ pCursor->pbCur += 2;
+
+ PRTASN1CURSOR pParentCursor = pCursor->pUp;
+ if ( pParentCursor
+ && (pParentCursor->fFlags & RTASN1CURSOR_FLAGS_INDEFINITE_LENGTH))
+ {
+ pParentCursor->pbCur -= pCursor->cbLeft;
+ pParentCursor->cbLeft += pCursor->cbLeft;
+ return VINF_SUCCESS;
+ }
+
+ if (pCursor->cbLeft == 0)
+ return VINF_SUCCESS;
+
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_NOT_AT_END,
+ "%u (%#x) bytes left over (parent not indefinite length)", pCursor->cbLeft, pCursor->cbLeft);
+ }
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_NOT_AT_END, "%u (%#x) bytes left over [indef: %.*Rhxs]",
+ pCursor->cbLeft, pCursor->cbLeft, RT_MIN(pCursor->cbLeft, 16), pCursor->pbCur);
+ }
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_NOT_AT_END,
+ "1 byte left over, expected two for indefinite length end-of-content sequence");
+}
+
+
+RTDECL(int) RTAsn1CursorCheckSeqEnd(PRTASN1CURSOR pCursor, PRTASN1SEQUENCECORE pSeqCore)
+{
+ return rtAsn1CursorCheckSeqOrSetEnd(pCursor, &pSeqCore->Asn1Core);
+}
+
+
+RTDECL(int) RTAsn1CursorCheckSetEnd(PRTASN1CURSOR pCursor, PRTASN1SETCORE pSetCore)
+{
+ return rtAsn1CursorCheckSeqOrSetEnd(pCursor, &pSetCore->Asn1Core);
+}
+
+
+RTDECL(int) RTAsn1CursorCheckOctStrEnd(PRTASN1CURSOR pCursor, PRTASN1OCTETSTRING pOctetString)
+{
+ return rtAsn1CursorCheckSeqOrSetEnd(pCursor, &pOctetString->Asn1Core);
+}
+
+
+RTDECL(PRTASN1ALLOCATION) RTAsn1CursorInitAllocation(PRTASN1CURSOR pCursor, PRTASN1ALLOCATION pAllocation)
+{
+ pAllocation->cbAllocated = 0;
+ pAllocation->cReallocs = 0;
+ pAllocation->uReserved0 = 0;
+ pAllocation->pAllocator = pCursor->pPrimary->pAllocator;
+ return pAllocation;
+}
+
+
+RTDECL(PRTASN1ARRAYALLOCATION) RTAsn1CursorInitArrayAllocation(PRTASN1CURSOR pCursor, PRTASN1ARRAYALLOCATION pAllocation,
+ 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 = pCursor->pPrimary->pAllocator;
+ return pAllocation;
+}
+
+
+RTDECL(int) RTAsn1CursorReadHdr(PRTASN1CURSOR pCursor, PRTASN1CORE pAsn1Core, const char *pszErrorTag)
+{
+ /*
+ * Initialize the return structure in case of failure.
+ */
+ pAsn1Core->uTag = 0;
+ pAsn1Core->fClass = 0;
+ pAsn1Core->uRealTag = 0;
+ pAsn1Core->fRealClass = 0;
+ pAsn1Core->cbHdr = 0;
+ pAsn1Core->cb = 0;
+ pAsn1Core->fFlags = 0;
+ pAsn1Core->uData.pv = NULL;
+ pAsn1Core->pOps = NULL;
+
+ /*
+ * The header has at least two bytes: Type & length.
+ */
+ if (pCursor->cbLeft >= 2)
+ {
+ uint32_t uTag = pCursor->pbCur[0];
+ uint32_t cb = pCursor->pbCur[1];
+ pCursor->cbLeft -= 2;
+ pCursor->pbCur += 2;
+
+ pAsn1Core->uRealTag = pAsn1Core->uTag = uTag & ASN1_TAG_MASK;
+ pAsn1Core->fRealClass = pAsn1Core->fClass = uTag & ~ASN1_TAG_MASK;
+ pAsn1Core->cbHdr = 2;
+ if ((uTag & ASN1_TAG_MASK) == ASN1_TAG_USE_LONG_FORM)
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_LONG_TAG,
+ "%s: Implement parsing of tags > 30: %#x (length=%#x)", pszErrorTag, uTag, cb);
+
+ /* Extended length field? */
+ if (cb & RT_BIT(7))
+ {
+ if (cb != RT_BIT(7))
+ {
+ /* Definite form. */
+ uint8_t cbEnc = cb & 0x7f;
+ if (cbEnc > pCursor->cbLeft)
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_BAD_LENGTH_ENCODING,
+ "%s: Extended BER length field longer than available data: %#x vs %#x (uTag=%#x)",
+ pszErrorTag, cbEnc, pCursor->cbLeft, uTag);
+ switch (cbEnc)
+ {
+ case 1:
+ cb = pCursor->pbCur[0];
+ break;
+ case 2:
+ cb = RT_MAKE_U16(pCursor->pbCur[1], pCursor->pbCur[0]);
+ break;
+ case 3:
+ cb = RT_MAKE_U32_FROM_U8(pCursor->pbCur[2], pCursor->pbCur[1], pCursor->pbCur[0], 0);
+ break;
+ case 4:
+ cb = RT_MAKE_U32_FROM_U8(pCursor->pbCur[3], pCursor->pbCur[2], pCursor->pbCur[1], pCursor->pbCur[0]);
+ break;
+ default:
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_BAD_LENGTH_ENCODING,
+ "%s: Too long/short extended BER length field: %#x (uTag=%#x)",
+ pszErrorTag, cbEnc, uTag);
+ }
+ pCursor->cbLeft -= cbEnc;
+ pCursor->pbCur += cbEnc;
+ pAsn1Core->cbHdr += cbEnc;
+
+ /* Check the length encoding efficiency (T-REC-X.690-200811 10.1, 9.1). */
+ if (pCursor->fFlags & (RTASN1CURSOR_FLAGS_DER | RTASN1CURSOR_FLAGS_CER))
+ {
+ if (cb <= 0x7f)
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_BAD_LENGTH_ENCODING,
+ "%s: Invalid DER/CER length encoding: cbEnc=%u cb=%#x uTag=%#x",
+ pszErrorTag, cbEnc, cb, uTag);
+ uint8_t cbNeeded;
+ if (cb <= 0x000000ff) cbNeeded = 1;
+ else if (cb <= 0x0000ffff) cbNeeded = 2;
+ else if (cb <= 0x00ffffff) cbNeeded = 3;
+ else cbNeeded = 4;
+ if (cbNeeded != cbEnc)
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_BAD_LENGTH_ENCODING,
+ "%s: Invalid DER/CER length encoding: cb=%#x uTag=%#x cbEnc=%u cbNeeded=%u",
+ pszErrorTag, cb, uTag, cbEnc, cbNeeded);
+ }
+ }
+ /* Indefinite form. */
+ else if (pCursor->fFlags & RTASN1CURSOR_FLAGS_DER)
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_ILLEGAL_INDEFINITE_LENGTH,
+ "%s: Indefinite length form not allowed in DER mode (uTag=%#x).", pszErrorTag, uTag);
+ else if (!(uTag & ASN1_TAGFLAG_CONSTRUCTED))
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_BAD_INDEFINITE_LENGTH,
+ "%s: Indefinite BER/CER encoding is for non-constructed tag (uTag=%#x)", pszErrorTag, uTag);
+ else if ( uTag != (ASN1_TAG_SEQUENCE | ASN1_TAGFLAG_CONSTRUCTED)
+ && uTag != (ASN1_TAG_SET | ASN1_TAGFLAG_CONSTRUCTED)
+ && (uTag & (ASN1_TAGFLAG_CONSTRUCTED | ASN1_TAGCLASS_CONTEXT))
+ != (ASN1_TAGFLAG_CONSTRUCTED | ASN1_TAGCLASS_CONTEXT) )
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_BAD_INDEFINITE_LENGTH,
+ "%s: Indefinite BER/CER encoding not supported for this tag (uTag=%#x)", pszErrorTag, uTag);
+ else if (pCursor->fFlags & RTASN1CURSOR_FLAGS_INDEFINITE_LENGTH)
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_BAD_INDEFINITE_LENGTH,
+ "%s: Nested indefinite BER/CER encoding. (uTag=%#x)", pszErrorTag, uTag);
+ else if (pCursor->cbLeft < 2)
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_BAD_INDEFINITE_LENGTH,
+ "%s: Too little data left for indefinite BER/CER encoding (uTag=%#x)", pszErrorTag, uTag);
+ else
+ {
+ pCursor->fFlags |= RTASN1CURSOR_FLAGS_INDEFINITE_LENGTH;
+ pAsn1Core->fFlags |= RTASN1CORE_F_INDEFINITE_LENGTH;
+ cb = pCursor->cbLeft; /* Start out with the whole sequence, adjusted later upon reach the end. */
+ }
+ }
+ /* else if (cb == 0 && uTag == 0) { end of content } - callers handle this */
+
+ /* Check if the length makes sense. */
+ if (cb > pCursor->cbLeft)
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_BAD_LENGTH,
+ "%s: BER value length out of bounds: %#x (max=%#x uTag=%#x)",
+ pszErrorTag, cb, pCursor->cbLeft, uTag);
+
+ pAsn1Core->fFlags |= RTASN1CORE_F_PRESENT | RTASN1CORE_F_DECODED_CONTENT;
+ pAsn1Core->cb = cb;
+ pAsn1Core->uData.pv = (void *)pCursor->pbCur;
+ return VINF_SUCCESS;
+ }
+
+ if (pCursor->cbLeft)
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_TOO_LITTLE_DATA_LEFT,
+ "%s: Too little data left to form a valid BER header", pszErrorTag);
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_NO_MORE_DATA,
+ "%s: No more data reading BER header", pszErrorTag);
+}
+
+
+RTDECL(int) RTAsn1CursorMatchTagClassFlagsEx(PRTASN1CURSOR pCursor, PRTASN1CORE pAsn1Core, uint32_t uTag, uint32_t fClass,
+ bool fString, uint32_t fFlags, const char *pszErrorTag, const char *pszWhat)
+{
+ if (pAsn1Core->uTag == uTag)
+ {
+ if (pAsn1Core->fClass == fClass)
+ return VINF_SUCCESS;
+ if ( fString
+ && pAsn1Core->fClass == (fClass | ASN1_TAGFLAG_CONSTRUCTED))
+ {
+ if (!(pCursor->fFlags & (RTASN1CURSOR_FLAGS_DER | RTASN1CURSOR_FLAGS_CER)))
+ return VINF_SUCCESS;
+ if (pCursor->fFlags & RTASN1CURSOR_FLAGS_CER)
+ {
+ if (pAsn1Core->cb > 1000)
+ return VINF_SUCCESS;
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_ILLEGAL_CONSTRUCTED_STRING,
+ "%s: Constructed %s only allowed for >1000 byte in CER encoding: cb=%#x uTag=%#x fClass=%#x",
+ pszErrorTag, pszWhat, pAsn1Core->cb, pAsn1Core->uTag, pAsn1Core->fClass);
+ }
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_ILLEGAL_CONSTRUCTED_STRING,
+ "%s: DER encoding does not allow constructed %s (cb=%#x uTag=%#x fClass=%#x)",
+ pszErrorTag, pszWhat, pAsn1Core->cb, pAsn1Core->uTag, pAsn1Core->fClass);
+ }
+ }
+
+ if (fFlags & RTASN1CURSOR_GET_F_IMPLICIT)
+ {
+ pAsn1Core->fFlags |= RTASN1CORE_F_TAG_IMPLICIT;
+ pAsn1Core->uRealTag = uTag;
+ pAsn1Core->fRealClass = fClass;
+ return VINF_SUCCESS;
+ }
+
+ return RTAsn1CursorSetInfo(pCursor, pAsn1Core->uTag != uTag ? VERR_ASN1_CURSOR_TAG_MISMATCH : VERR_ASN1_CURSOR_TAG_FLAG_CLASS_MISMATCH,
+ "%s: Unexpected %s type/flags: %#x/%#x (expected %#x/%#x)",
+ pszErrorTag, pszWhat, pAsn1Core->uTag, pAsn1Core->fClass, uTag, fClass);
+}
+
+
+
+static int rtAsn1CursorGetXxxxCursor(PRTASN1CURSOR pCursor, uint32_t fFlags, uint32_t uTag, uint8_t fClass,
+ PRTASN1CORE pAsn1Core, PRTASN1CURSOR pRetCursor,
+ const char *pszErrorTag, const char *pszWhat)
+{
+ int rc = RTAsn1CursorReadHdr(pCursor, pAsn1Core, pszErrorTag);
+ if (RT_SUCCESS(rc))
+ {
+ if ( pAsn1Core->uTag == uTag
+ && pAsn1Core->fClass == fClass)
+ rc = VINF_SUCCESS;
+ else if (fFlags & RTASN1CURSOR_GET_F_IMPLICIT)
+ {
+ pAsn1Core->fFlags |= RTASN1CORE_F_TAG_IMPLICIT;
+ pAsn1Core->uRealTag = uTag;
+ pAsn1Core->fRealClass = fClass;
+ rc = VINF_SUCCESS;
+ }
+ else
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_ILLEGAL_CONSTRUCTED_STRING,
+ "%s: Unexpected %s type/flags: %#x/%#x (expected %#x/%#x)",
+ pszErrorTag, pszWhat, pAsn1Core->uTag, pAsn1Core->fClass, uTag, fClass);
+ rc = RTAsn1CursorInitSub(pCursor, pAsn1Core->cb, pRetCursor, pszErrorTag);
+ if (RT_SUCCESS(rc))
+ {
+ pAsn1Core->fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
+ return VINF_SUCCESS;
+ }
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1CursorGetSequenceCursor(PRTASN1CURSOR pCursor, uint32_t fFlags,
+ PRTASN1SEQUENCECORE pSeqCore, PRTASN1CURSOR pSeqCursor, const char *pszErrorTag)
+{
+ return rtAsn1CursorGetXxxxCursor(pCursor, fFlags, ASN1_TAG_SEQUENCE, ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_CONSTRUCTED,
+ &pSeqCore->Asn1Core, pSeqCursor, pszErrorTag, "sequence");
+}
+
+
+RTDECL(int) RTAsn1CursorGetSetCursor(PRTASN1CURSOR pCursor, uint32_t fFlags,
+ PRTASN1SETCORE pSetCore, PRTASN1CURSOR pSetCursor, const char *pszErrorTag)
+{
+ return rtAsn1CursorGetXxxxCursor(pCursor, fFlags, ASN1_TAG_SET, ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_CONSTRUCTED,
+ &pSetCore->Asn1Core, pSetCursor, pszErrorTag, "set");
+}
+
+
+RTDECL(int) RTAsn1CursorGetContextTagNCursor(PRTASN1CURSOR pCursor, uint32_t fFlags, uint32_t uExpectedTag,
+ PCRTASN1COREVTABLE pVtable, PRTASN1CONTEXTTAG pCtxTag, PRTASN1CURSOR pCtxTagCursor,
+ const char *pszErrorTag)
+{
+ int rc = rtAsn1CursorGetXxxxCursor(pCursor, fFlags, uExpectedTag, ASN1_TAGCLASS_CONTEXT | ASN1_TAGFLAG_CONSTRUCTED,
+ &pCtxTag->Asn1Core, pCtxTagCursor, pszErrorTag, "ctx tag");
+ pCtxTag->Asn1Core.pOps = pVtable;
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1CursorPeek(PRTASN1CURSOR pCursor, PRTASN1CORE pAsn1Core)
+{
+ uint32_t cbSavedLeft = pCursor->cbLeft;
+ uint8_t const *pbSavedCur = pCursor->pbCur;
+ uint8_t const fSavedFlags = pCursor->fFlags;
+ PRTERRINFO const pErrInfo = pCursor->pPrimary->pErrInfo;
+ pCursor->pPrimary->pErrInfo = NULL;
+
+ int rc = RTAsn1CursorReadHdr(pCursor, pAsn1Core, "peek");
+
+ pCursor->pPrimary->pErrInfo = pErrInfo;
+ pCursor->pbCur = pbSavedCur;
+ pCursor->cbLeft = cbSavedLeft;
+ pCursor->fFlags = fSavedFlags;
+ return rc;
+}
+
+
+RTDECL(bool) RTAsn1CursorIsNextEx(PRTASN1CURSOR pCursor, uint32_t uTag, uint8_t fClass)
+{
+ RTASN1CORE Asn1Core;
+ int rc = RTAsn1CursorPeek(pCursor, &Asn1Core);
+ if (RT_SUCCESS(rc))
+ return uTag == Asn1Core.uTag
+ && fClass == Asn1Core.fClass;
+ return false;
+}
+
+
+/** @name Legacy Interfaces.
+ * @{ */
+RTDECL(int) RTAsn1CursorGetCore(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1CORE pAsn1Core, const char *pszErrorTag)
+{
+ return RTAsn1Core_DecodeAsn1(pCursor, fFlags, pAsn1Core, pszErrorTag);
+}
+
+
+RTDECL(int) RTAsn1CursorGetNull(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1NULL pNull, const char *pszErrorTag)
+{
+ return RTAsn1Null_DecodeAsn1(pCursor, fFlags, pNull, pszErrorTag);
+}
+
+
+RTDECL(int) RTAsn1CursorGetInteger(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1INTEGER pInteger, const char *pszErrorTag)
+{
+ return RTAsn1Integer_DecodeAsn1(pCursor, fFlags, pInteger, pszErrorTag);
+}
+
+
+RTDECL(int) RTAsn1CursorGetBoolean(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1BOOLEAN pBoolean, const char *pszErrorTag)
+{
+ return RTAsn1Boolean_DecodeAsn1(pCursor, fFlags, pBoolean, pszErrorTag);
+}
+
+
+RTDECL(int) RTAsn1CursorGetObjId(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1OBJID pObjId, const char *pszErrorTag)
+{
+ return RTAsn1ObjId_DecodeAsn1(pCursor, fFlags, pObjId, pszErrorTag);
+}
+
+
+RTDECL(int) RTAsn1CursorGetTime(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1TIME pTime, const char *pszErrorTag)
+{
+ return RTAsn1Time_DecodeAsn1(pCursor, fFlags, pTime, pszErrorTag);
+}
+
+
+RTDECL(int) RTAsn1CursorGetBitStringEx(PRTASN1CURSOR pCursor, uint32_t fFlags, uint32_t cMaxBits, PRTASN1BITSTRING pBitString,
+ const char *pszErrorTag)
+{
+ return RTAsn1BitString_DecodeAsn1Ex(pCursor, fFlags, cMaxBits, pBitString, pszErrorTag);
+}
+
+
+RTDECL(int) RTAsn1CursorGetBitString(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1BITSTRING pBitString, const char *pszErrorTag)
+{
+ return RTAsn1BitString_DecodeAsn1(pCursor, fFlags, pBitString, pszErrorTag);
+}
+
+
+RTDECL(int) RTAsn1CursorGetOctetString(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1OCTETSTRING pOctetString,
+ const char *pszErrorTag)
+{
+ return RTAsn1OctetString_DecodeAsn1(pCursor, fFlags, pOctetString, pszErrorTag);
+}
+
+
+RTDECL(int) RTAsn1CursorGetString(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1STRING pString, const char *pszErrorTag)
+{
+ return RTAsn1String_DecodeAsn1(pCursor, fFlags, pString, pszErrorTag);
+}
+
+
+RTDECL(int) RTAsn1CursorGetIa5String(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1STRING pString, const char *pszErrorTag)
+{
+ return RTAsn1Ia5String_DecodeAsn1(pCursor, fFlags, pString, pszErrorTag);
+}
+
+
+RTDECL(int) RTAsn1CursorGetUtf8String(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1STRING pString, const char *pszErrorTag)
+{
+ return RTAsn1Utf8String_DecodeAsn1(pCursor, fFlags, pString, pszErrorTag);
+}
+
+
+RTDECL(int) RTAsn1CursorGetBmpString(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1STRING pString, const char *pszErrorTag)
+{
+ return RTAsn1BmpString_DecodeAsn1(pCursor, fFlags, pString, pszErrorTag);
+}
+
+
+RTDECL(int) RTAsn1CursorGetDynType(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1DYNTYPE pDynType, const char *pszErrorTag)
+{
+ return RTAsn1DynType_DecodeAsn1(pCursor, fFlags, pDynType, pszErrorTag);
+}
+/** @} */
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-default-allocator.cpp b/src/VBox/Runtime/common/asn1/asn1-default-allocator.cpp
new file mode 100644
index 00000000..ba04a854
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-default-allocator.cpp
@@ -0,0 +1,231 @@
+/* $Id: asn1-default-allocator.cpp $ */
+/** @file
+ * IPRT - ASN.1, Default Allocator.
+ */
+
+/*
+ * 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/mem.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+
+/**
+ * Aligns allocation sizes a little.
+ *
+ * @returns Aligned size.
+ * @param cb Requested size.
+ */
+static size_t rtAsn1DefaultAllocator_AlignSize(size_t cb)
+{
+ if (cb >= 64)
+ return RT_ALIGN_Z(cb, 64);
+ if (cb >= 32)
+ return RT_ALIGN_Z(cb, 32);
+ if (cb >= 16)
+ return RT_ALIGN_Z(cb, 16);
+ return cb;
+}
+
+
+/** @interface_method_impl{RTASN1ALLOCATORVTABLE,pfnFree} */
+static DECLCALLBACK(void) rtAsn1DefaultAllocator_Free(PCRTASN1ALLOCATORVTABLE pThis, PRTASN1ALLOCATION pAllocation, void *pv)
+{
+ RT_NOREF_PV(pThis);
+ RTMemFree(pv);
+ pAllocation->cbAllocated = 0;
+}
+
+
+/** @interface_method_impl{RTASN1ALLOCATORVTABLE,pfnAlloc} */
+static DECLCALLBACK(int) rtAsn1DefaultAllocator_Alloc(PCRTASN1ALLOCATORVTABLE pThis, PRTASN1ALLOCATION pAllocation,
+ void **ppv, size_t cb)
+{
+ size_t cbAlloc = rtAsn1DefaultAllocator_AlignSize(cb);
+ void *pv = RTMemAllocZ(cbAlloc);
+ if (pv)
+ {
+ *ppv = pv;
+ pAllocation->cbAllocated = (uint32_t)cbAlloc;
+ return VINF_SUCCESS;
+ }
+ RT_NOREF_PV(pThis);
+ return VERR_NO_MEMORY;
+}
+
+
+/** @interface_method_impl{RTASN1ALLOCATORVTABLE,pfnRealloc} */
+static DECLCALLBACK(int) rtAsn1DefaultAllocator_Realloc(PCRTASN1ALLOCATORVTABLE pThis, PRTASN1ALLOCATION pAllocation,
+ void *pvOld, void **ppvNew, size_t cbNew)
+{
+ Assert(pvOld);
+ Assert(cbNew);
+ size_t cbAlloc = rtAsn1DefaultAllocator_AlignSize(cbNew);
+ void *pv = RTMemRealloc(pvOld, cbAlloc);
+ if (pv)
+ {
+ *ppvNew = pv;
+ pAllocation->cbAllocated = (uint32_t)cbAlloc;
+ return VINF_SUCCESS;
+ }
+ RT_NOREF_PV(pThis);
+ return VERR_NO_MEMORY;
+}
+
+
+/** @interface_method_impl{RTASN1ALLOCATORVTABLE,pfnFreeArray} */
+static DECLCALLBACK(void) rtAsn1DefaultAllocator_FreeArray(PCRTASN1ALLOCATORVTABLE pThis, PRTASN1ARRAYALLOCATION pAllocation,
+ void **papvArray)
+{
+ RT_NOREF_PV(pThis);
+ Assert(papvArray);
+ Assert(pAllocation->cbEntry);
+
+ uint32_t i = pAllocation->cEntriesAllocated;
+ while (i-- > 0)
+ RTMemFree(papvArray[i]);
+ RTMemFree(papvArray);
+
+ pAllocation->cEntriesAllocated = 0;
+ pAllocation->cPointersAllocated = 0;
+}
+
+
+/** @interface_method_impl{RTASN1ALLOCATORVTABLE,pfnGrowArray} */
+static DECLCALLBACK(int) rtAsn1DefaultAllocator_GrowArray(PCRTASN1ALLOCATORVTABLE pThis, PRTASN1ARRAYALLOCATION pAllocation,
+ void ***ppapvArray, uint32_t cMinEntries)
+{
+ RT_NOREF_PV(pThis);
+
+ /*
+ * Resize the pointer array. We do chunks of 64 bytes for now.
+ */
+ void **papvArray = *ppapvArray;
+ uint32_t cPointers = RT_ALIGN_32(cMinEntries, 64 / sizeof(void *));
+ if (cPointers > pAllocation->cPointersAllocated)
+ {
+ void *pvPointers = RTMemRealloc(papvArray, cPointers * sizeof(void *));
+ if (pvPointers)
+ { /* likely */ }
+ else if (cMinEntries > pAllocation->cPointersAllocated)
+ {
+ cPointers = cMinEntries;
+ pvPointers = RTMemRealloc(*ppapvArray, cPointers * sizeof(void *));
+ if (!pvPointers)
+ return VERR_NO_MEMORY;
+ }
+ else
+ {
+ cPointers = pAllocation->cPointersAllocated;
+ pvPointers = papvArray;
+ }
+
+ *ppapvArray = papvArray = (void **)pvPointers;
+ RT_BZERO(&papvArray[pAllocation->cPointersAllocated], (cPointers - pAllocation->cPointersAllocated) * sizeof(void *));
+ pAllocation->cPointersAllocated = cPointers;
+ }
+
+ /*
+ * Add more entries. Do multiple as the array grows.
+ *
+ * Note! We could possibly optimize this by allocating slabs of entries and
+ * slice them up. However, keep things as simple as possible for now.
+ */
+ uint32_t cEntries = cMinEntries;
+ if (cEntries > 2)
+ {
+ if (cEntries > 8)
+ cEntries = RT_ALIGN_32(cEntries, 4);
+ else
+ cEntries = RT_ALIGN_32(cEntries, 2);
+ cEntries = RT_MIN(cEntries, cPointers);
+ Assert(cEntries >= cMinEntries);
+ }
+ Assert(cEntries <= pAllocation->cPointersAllocated);
+
+ while (pAllocation->cEntriesAllocated < cEntries)
+ {
+ void *pv;
+ papvArray[pAllocation->cEntriesAllocated] = pv = RTMemAllocZ(pAllocation->cbEntry);
+ if (pv)
+ pAllocation->cEntriesAllocated++;
+ else if (pAllocation->cEntriesAllocated >= cMinEntries)
+ break;
+ else
+ return VERR_NO_MEMORY;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTASN1ALLOCATORVTABLE,pfnShrinkArray} */
+static DECLCALLBACK(void) rtAsn1DefaultAllocator_ShrinkArray(PCRTASN1ALLOCATORVTABLE pThis, PRTASN1ARRAYALLOCATION pAllocation,
+ void ***ppapvArray, uint32_t cNew, uint32_t cCurrent)
+{
+ RT_NOREF_PV(pThis);
+
+ /*
+ * For now we only zero the entries being removed.
+ */
+ void **papvArray = *ppapvArray;
+ while (cNew < cCurrent)
+ {
+ RT_BZERO(papvArray[cNew], pAllocation->cbEntry);
+ cNew++;
+ }
+}
+
+
+
+/** The default ASN.1 allocator. */
+#if 1 || !defined(IN_RING3) || defined(DOXYGEN_RUNNING)
+RT_DECL_DATA_CONST(RTASN1ALLOCATORVTABLE const) g_RTAsn1DefaultAllocator =
+#else
+RT_DECL_DATA_CONST(RTASN1ALLOCATORVTABLE const) g_RTAsn1DefaultAllocatorDisabled =
+#endif
+{
+ rtAsn1DefaultAllocator_Free,
+ rtAsn1DefaultAllocator_Alloc,
+ rtAsn1DefaultAllocator_Realloc,
+ rtAsn1DefaultAllocator_FreeArray,
+ rtAsn1DefaultAllocator_GrowArray,
+ rtAsn1DefaultAllocator_ShrinkArray
+};
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-dump.cpp b/src/VBox/Runtime/common/asn1/asn1-dump.cpp
new file mode 100644
index 00000000..e75d379e
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-dump.cpp
@@ -0,0 +1,644 @@
+/* $Id: asn1-dump.cpp $ */
+/** @file
+ * IPRT - ASN.1, Structure Dumper.
+ */
+
+/*
+ * 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/errcore.h>
+#include <iprt/log.h>
+#ifdef IN_RING3
+# include <iprt/stream.h>
+#endif
+#include <iprt/string.h>
+
+#include <iprt/formats/asn1.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Dump data structure.
+ */
+typedef struct RTASN1DUMPDATA
+{
+ /** RTASN1DUMP_F_XXX. */
+ uint32_t fFlags;
+ /** The printfv like output function. */
+ PFNRTDUMPPRINTFV pfnPrintfV;
+ /** PrintfV user argument. */
+ void *pvUser;
+} RTASN1DUMPDATA;
+/** Pointer to a dump data structure. */
+typedef RTASN1DUMPDATA *PRTASN1DUMPDATA;
+
+
+#ifndef IN_SUP_HARDENED_R3
+
+/*
+ * Since we're the only user of OIDs, this stuff lives here.
+ * Should that ever change, this code needs to move elsewhere and get it's own public API.
+ */
+# include "oiddb.h"
+
+
+/**
+ * Searches a range in the big table for a key.
+ *
+ * @returns Pointer to the matching entry. NULL if not found.
+ * @param iEntry The start of the range.
+ * @param cEntries The number of entries in the range.
+ * @param uKey The key to find.
+ */
+DECLINLINE(PCRTOIDENTRYBIG) rtOidDbLookupBig(uint32_t iEntry, uint32_t cEntries, uint32_t uKey)
+{
+ /* Not worth doing binary search here, too few entries. */
+ while (cEntries-- > 0)
+ {
+ uint32_t const uThisKey = g_aBigOidTable[iEntry].uKey;
+ if (uThisKey >= uKey)
+ {
+ if (uThisKey == uKey)
+ return &g_aBigOidTable[iEntry];
+ break;
+ }
+ iEntry++;
+ }
+ return NULL;
+}
+
+
+/**
+ * Searches a range in the small table for a key.
+ *
+ * @returns Pointer to the matching entry. NULL if not found.
+ * @param iEntry The start of the range.
+ * @param cEntries The number of entries in the range.
+ * @param uKey The key to find.
+ */
+DECLINLINE(PCRTOIDENTRYSMALL) rtOidDbLookupSmall(uint32_t iEntry, uint32_t cEntries, uint32_t uKey)
+{
+ if (cEntries < 6)
+ {
+ /* Linear search for small ranges. */
+ while (cEntries-- > 0)
+ {
+ uint32_t const uThisKey = g_aSmallOidTable[iEntry].uKey;
+ if (uThisKey >= uKey)
+ {
+ if (uThisKey == uKey)
+ return &g_aSmallOidTable[iEntry];
+ break;
+ }
+ iEntry++;
+ }
+ }
+ else
+ {
+ /* Binary search. */
+ uint32_t iEnd = iEntry + cEntries;
+ for (;;)
+ {
+ uint32_t const i = iEntry + (iEnd - iEntry) / 2;
+ uint32_t const uThisKey = g_aSmallOidTable[i].uKey;
+ if (uThisKey < uKey)
+ {
+ iEntry = i + 1;
+ if (iEntry >= iEnd)
+ break;
+ }
+ else if (uThisKey > uKey)
+ {
+ iEnd = i;
+ if (iEnd <= iEntry)
+ break;
+ }
+ else
+ return &g_aSmallOidTable[i];
+ }
+ }
+ return NULL;
+}
+
+
+/**
+ * Queries the name for an object identifier.
+ *
+ * @returns IPRT status code (VINF_SUCCESS, VERR_NOT_FOUND,
+ * VERR_BUFFER_OVERFLOW)
+ * @param pauComponents The components making up the object ID.
+ * @param cComponents The number of components.
+ * @param pszDst Where to store the name if found.
+ * @param cbDst The size of the destination buffer.
+ */
+static int rtOidDbQueryObjIdName(uint32_t const *pauComponents, uint8_t cComponents, char *pszDst, size_t cbDst)
+{
+ int rc = VERR_NOT_FOUND;
+ if (cComponents > 0)
+ {
+ /*
+ * The top level is always in the small table as the range is restricted to 0,1,2.
+ */
+ bool fBigTable = false;
+ uint32_t cEntries = RT_MIN(RT_ELEMENTS(g_aSmallOidTable), 3);
+ uint32_t iEntry = 0;
+ for (;;)
+ {
+ uint32_t const uKey = *pauComponents++;
+ if (!fBigTable)
+ {
+ PCRTOIDENTRYSMALL pSmallHit = rtOidDbLookupSmall(iEntry, cEntries, uKey);
+ if (pSmallHit)
+ {
+ if (--cComponents == 0)
+ {
+ if (RTBldProgStrTabQueryString(&g_OidDbStrTab, pSmallHit->offString,
+ pSmallHit->cchString, pszDst, cbDst) >= 0)
+ return VINF_SUCCESS;
+ rc = VERR_BUFFER_OVERFLOW;
+ break;
+ }
+ cEntries = pSmallHit->cChildren;
+ if (cEntries)
+ {
+ iEntry = pSmallHit->idxChildren;
+ fBigTable = pSmallHit->fBigTable;
+ continue;
+ }
+ }
+ }
+ else
+ {
+ PCRTOIDENTRYBIG pBigHit = rtOidDbLookupBig(iEntry, cEntries, uKey);
+ if (pBigHit)
+ {
+ if (--cComponents == 0)
+ {
+ if (RTBldProgStrTabQueryString(&g_OidDbStrTab, pBigHit->offString,
+ pBigHit->cchString, pszDst, cbDst) >= 0)
+ return VINF_SUCCESS;
+ rc = VERR_BUFFER_OVERFLOW;
+ break;
+ }
+ cEntries = pBigHit->cChildren;
+ if (cEntries)
+ {
+ iEntry = pBigHit->idxChildren;
+ fBigTable = pBigHit->fBigTable;
+ continue;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Queries the name for an object identifier.
+ *
+ * This API is simple and more or less requires a
+ *
+ * @returns IPRT status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_NOT_FOUND if not found.
+ * @retval VERR_BUFFER_OVERFLOW if more buffer space is required.
+ *
+ * @param pauComponents The components making up the object ID.
+ * @param cComponents The number of components.
+ * @param pszDst Where to store the name if found.
+ * @param cbDst The size of the destination buffer.
+ */
+RTDECL(int) RTAsn1QueryObjIdName(PCRTASN1OBJID pObjId, char *pszDst, size_t cbDst)
+{
+ return rtOidDbQueryObjIdName(pObjId->pauComponents, pObjId->cComponents, pszDst, cbDst);
+}
+
+#endif /* !IN_SUP_HARDENED_R3 */
+
+
+
+/**
+ * Wrapper around FNRTASN1DUMPPRINTFV.
+ *
+ * @param pData The dump data structure.
+ * @param pszFormat Format string.
+ * @param ... Format arguments.
+ */
+static void rtAsn1DumpPrintf(PRTASN1DUMPDATA pData, const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ pData->pfnPrintfV(pData->pvUser, pszFormat, va);
+ va_end(va);
+}
+
+
+/**
+ * Prints indentation.
+ *
+ * @param pData The dump data structure.
+ * @param uDepth The indentation depth.
+ */
+static void rtAsn1DumpPrintIdent(PRTASN1DUMPDATA pData, uint32_t uDepth)
+{
+ uint32_t cchLeft = uDepth * 2;
+ while (cchLeft > 0)
+ {
+ static char const s_szSpaces[] = " ";
+ uint32_t cch = RT_MIN(cchLeft, sizeof(s_szSpaces) - 1);
+ rtAsn1DumpPrintf(pData, &s_szSpaces[sizeof(s_szSpaces) - 1 - cch]);
+ cchLeft -= cch;
+ }
+}
+
+
+/**
+ * Dumps UTC TIME and GENERALIZED TIME
+ *
+ * @param pData The dump data structure.
+ * @param pAsn1Core The ASN.1 core object representation.
+ * @param pszType The time type name.
+ */
+static void rtAsn1DumpTime(PRTASN1DUMPDATA pData, PCRTASN1CORE pAsn1Core, const char *pszType)
+{
+ if ((pAsn1Core->fFlags & RTASN1CORE_F_PRIMITE_TAG_STRUCT))
+ {
+ PCRTASN1TIME pTime = (PCRTASN1TIME)pAsn1Core;
+ rtAsn1DumpPrintf(pData, "%s -- %04u-%02u-%02u %02u:%02u:%02.%09Z\n",
+ pszType,
+ pTime->Time.i32Year, pTime->Time.u8Month, pTime->Time.u8MonthDay,
+ pTime->Time.u8Hour, pTime->Time.u8Minute, pTime->Time.u8Second,
+ pTime->Time.u32Nanosecond);
+ }
+ else if (pAsn1Core->cb > 0 && pAsn1Core->cb < 32 && pAsn1Core->uData.pch)
+ rtAsn1DumpPrintf(pData, "%s '%.*s'\n", pszType, (size_t)pAsn1Core->cb, pAsn1Core->uData.pch);
+ else
+ rtAsn1DumpPrintf(pData, "%s -- cb=%u\n", pszType, pAsn1Core->cb);
+}
+
+
+/**
+ * Dumps strings sharing the RTASN1STRING structure.
+ *
+ * @param pData The dump data structure.
+ * @param pAsn1Core The ASN.1 core object representation.
+ * @param pszType The string type name.
+ * @param uDepth The current identation level.
+ */
+static void rtAsn1DumpString(PRTASN1DUMPDATA pData, PCRTASN1CORE pAsn1Core, const char *pszType, uint32_t uDepth)
+{
+ rtAsn1DumpPrintf(pData, "%s", pszType);
+
+ const char *pszPostfix = "'\n";
+ bool fUtf8 = false;
+ const char *pch = pAsn1Core->uData.pch;
+ uint32_t cch = pAsn1Core->cb;
+ PCRTASN1STRING pString = (PCRTASN1STRING)pAsn1Core;
+ if ( (pAsn1Core->fFlags & RTASN1CORE_F_PRIMITE_TAG_STRUCT)
+ && pString->pszUtf8
+ && pString->cchUtf8)
+ {
+ fUtf8 = true;
+ pszPostfix = "' -- utf-8\n";
+ }
+
+ if (cch == 0 || !pch)
+ rtAsn1DumpPrintf(pData, "-- cb=%u\n", pszType, pAsn1Core->cb);
+ else
+ {
+ if (cch >= 48)
+ {
+ rtAsn1DumpPrintf(pData, "\n");
+ rtAsn1DumpPrintIdent(pData, uDepth + 1);
+ }
+ rtAsn1DumpPrintf(pData, " '");
+
+ /** @todo Handle BMP and UNIVERSIAL strings specially. */
+ do
+ {
+ const char *pchStart = pch;
+ while ( cch > 0
+ && (uint8_t)*pch >= 0x20
+ && (!fUtf8 ? (uint8_t)*pch < 0x7f : (uint8_t)*pch != 0x7f)
+ && *pch != '\'')
+ cch--, pch++;
+ if (pchStart != pch)
+ rtAsn1DumpPrintf(pData, "%.*s", pch - pchStart, pchStart);
+
+ while ( cch > 0
+ && ( (uint8_t)*pch < 0x20
+ || (!fUtf8 ? (uint8_t)*pch >= 0x7f : (uint8_t)*pch == 0x7f)
+ || (uint8_t)*pch == '\'') )
+ {
+ rtAsn1DumpPrintf(pData, "\\x%02x", *pch);
+ cch--;
+ pch++;
+ }
+ } while (cch > 0);
+
+ rtAsn1DumpPrintf(pData, pszPostfix);
+ }
+}
+
+
+/**
+ * Dumps the type and value of an universal ASN.1 type.
+ *
+ * @returns True if it opens a child, false if not.
+ * @param pData The dumper data.
+ * @param pAsn1Core The ASN.1 object to dump.
+ * @param uDepth The current depth (for indentation).
+ */
+static bool rtAsn1DumpUniversalTypeAndValue(PRTASN1DUMPDATA pData, PCRTASN1CORE pAsn1Core, uint32_t uDepth)
+{
+ const char *pszValuePrefix = "-- value:";
+ const char *pszDefault = "";
+ if (pAsn1Core->fFlags & RTASN1CORE_F_DEFAULT)
+ {
+ pszValuePrefix = "DEFAULT";
+ pszDefault = "DEFAULT ";
+ }
+
+ bool fOpen = false;
+ switch (pAsn1Core->uRealTag)
+ {
+ case ASN1_TAG_BOOLEAN:
+ if (pAsn1Core->fFlags & RTASN1CORE_F_PRIMITE_TAG_STRUCT)
+ rtAsn1DumpPrintf(pData, "BOOLEAN %s %RTbool\n", pszValuePrefix, ((PCRTASN1BOOLEAN)pAsn1Core)->fValue);
+ else if (pAsn1Core->cb == 1 && pAsn1Core->uData.pu8)
+ rtAsn1DumpPrintf(pData, "BOOLEAN %s %u\n", pszValuePrefix, *pAsn1Core->uData.pu8);
+ else
+ rtAsn1DumpPrintf(pData, "BOOLEAN -- cb=%u\n", pAsn1Core->cb);
+ break;
+
+ case ASN1_TAG_INTEGER:
+ if ((pAsn1Core->fFlags & RTASN1CORE_F_PRIMITE_TAG_STRUCT) && pAsn1Core->cb <= 8)
+ rtAsn1DumpPrintf(pData, "INTEGER %s %llu / %#llx\n", pszValuePrefix,
+ ((PCRTASN1INTEGER)pAsn1Core)->uValue, ((PCRTASN1INTEGER)pAsn1Core)->uValue);
+ else if (pAsn1Core->cb == 0 || pAsn1Core->cb >= 512 || !pAsn1Core->uData.pu8)
+ rtAsn1DumpPrintf(pData, "INTEGER -- cb=%u\n", pAsn1Core->cb);
+ else if (pAsn1Core->cb <= 32)
+ rtAsn1DumpPrintf(pData, "INTEGER %s %.*Rhxs\n", pszValuePrefix, (size_t)pAsn1Core->cb, pAsn1Core->uData.pu8);
+ else
+ rtAsn1DumpPrintf(pData, "INTEGER %s\n%.*Rhxd\n", pszValuePrefix, (size_t)pAsn1Core->cb, pAsn1Core->uData.pu8);
+ break;
+
+ case ASN1_TAG_BIT_STRING:
+ if ((pAsn1Core->fFlags & RTASN1CORE_F_PRIMITE_TAG_STRUCT))
+ {
+ PCRTASN1BITSTRING pBitString = (PCRTASN1BITSTRING)pAsn1Core;
+ rtAsn1DumpPrintf(pData, "BIT STRING %s-- cb=%u cBits=%#x cMaxBits=%#x",
+ pszDefault, pBitString->Asn1Core.cb, pBitString->cBits, pBitString->cMaxBits);
+ if (pBitString->cBits <= 64)
+ rtAsn1DumpPrintf(pData, " value=%#llx\n", RTAsn1BitString_GetAsUInt64(pBitString));
+ else
+ rtAsn1DumpPrintf(pData, "\n");
+ }
+ else
+ rtAsn1DumpPrintf(pData, "BIT STRING %s-- cb=%u\n", pszDefault, pAsn1Core->cb);
+ fOpen = pAsn1Core->pOps != NULL;
+ break;
+
+ case ASN1_TAG_OCTET_STRING:
+ rtAsn1DumpPrintf(pData, "OCTET STRING %s-- cb=%u\n", pszDefault, pAsn1Core->cb);
+ fOpen = pAsn1Core->pOps != NULL;
+ break;
+
+ case ASN1_TAG_NULL:
+ rtAsn1DumpPrintf(pData, "NULL\n");
+ break;
+
+ case ASN1_TAG_OID:
+ if ((pAsn1Core->fFlags & RTASN1CORE_F_PRIMITE_TAG_STRUCT))
+ {
+#ifndef IN_SUP_HARDENED_R3
+ PCRTASN1OBJID pObjId = (PCRTASN1OBJID)pAsn1Core;
+ char szName[64];
+ if (rtOidDbQueryObjIdName(pObjId->pauComponents, pObjId->cComponents, szName, sizeof(szName)) == VINF_SUCCESS)
+ rtAsn1DumpPrintf(pData, "OBJECT IDENTIFIER %s%s ('%s')\n",
+ pszDefault, szName, ((PCRTASN1OBJID)pAsn1Core)->szObjId);
+ else
+#endif
+ rtAsn1DumpPrintf(pData, "OBJECT IDENTIFIER %s'%s'\n", pszDefault, ((PCRTASN1OBJID)pAsn1Core)->szObjId);
+ }
+ else
+ rtAsn1DumpPrintf(pData, "OBJECT IDENTIFIER %s -- cb=%u\n", pszDefault, pAsn1Core->cb);
+ break;
+
+ case ASN1_TAG_OBJECT_DESCRIPTOR:
+ rtAsn1DumpPrintf(pData, "OBJECT DESCRIPTOR -- cb=%u TODO\n", pAsn1Core->cb);
+ break;
+
+ case ASN1_TAG_EXTERNAL:
+ rtAsn1DumpPrintf(pData, "EXTERNAL -- cb=%u TODO\n", pAsn1Core->cb);
+ break;
+
+ case ASN1_TAG_REAL:
+ rtAsn1DumpPrintf(pData, "REAL -- cb=%u TODO\n", pAsn1Core->cb);
+ break;
+
+ case ASN1_TAG_ENUMERATED:
+ rtAsn1DumpPrintf(pData, "ENUMERATED -- cb=%u TODO\n", pAsn1Core->cb);
+ break;
+
+ case ASN1_TAG_EMBEDDED_PDV:
+ rtAsn1DumpPrintf(pData, "EMBEDDED PDV -- cb=%u TODO\n", pAsn1Core->cb);
+ break;
+
+ case ASN1_TAG_UTF8_STRING:
+ rtAsn1DumpString(pData, pAsn1Core, "UTF8 STRING", uDepth);
+ break;
+
+ case ASN1_TAG_RELATIVE_OID:
+ rtAsn1DumpPrintf(pData, "RELATIVE OBJECT IDENTIFIER -- cb=%u TODO\n", pAsn1Core->cb);
+ break;
+
+ case ASN1_TAG_SEQUENCE:
+ rtAsn1DumpPrintf(pData, "SEQUENCE -- cb=%u\n", pAsn1Core->cb);
+ fOpen = true;
+ break;
+ case ASN1_TAG_SET:
+ rtAsn1DumpPrintf(pData, "SET -- cb=%u\n", pAsn1Core->cb);
+ fOpen = true;
+ break;
+
+ case ASN1_TAG_NUMERIC_STRING:
+ rtAsn1DumpString(pData, pAsn1Core, "NUMERIC STRING", uDepth);
+ break;
+
+ case ASN1_TAG_PRINTABLE_STRING:
+ rtAsn1DumpString(pData, pAsn1Core, "PRINTABLE STRING", uDepth);
+ break;
+
+ case ASN1_TAG_T61_STRING:
+ rtAsn1DumpString(pData, pAsn1Core, "T61 STRING", uDepth);
+ break;
+
+ case ASN1_TAG_VIDEOTEX_STRING:
+ rtAsn1DumpString(pData, pAsn1Core, "VIDEOTEX STRING", uDepth);
+ break;
+
+ case ASN1_TAG_IA5_STRING:
+ rtAsn1DumpString(pData, pAsn1Core, "IA5 STRING", uDepth);
+ break;
+
+ case ASN1_TAG_GRAPHIC_STRING:
+ rtAsn1DumpString(pData, pAsn1Core, "GRAPHIC STRING", uDepth);
+ break;
+
+ case ASN1_TAG_VISIBLE_STRING:
+ rtAsn1DumpString(pData, pAsn1Core, "VISIBLE STRING", uDepth);
+ break;
+
+ case ASN1_TAG_GENERAL_STRING:
+ rtAsn1DumpString(pData, pAsn1Core, "GENERAL STRING", uDepth);
+ break;
+
+ case ASN1_TAG_UNIVERSAL_STRING:
+ rtAsn1DumpString(pData, pAsn1Core, "UNIVERSAL STRING", uDepth);
+ break;
+
+ case ASN1_TAG_BMP_STRING:
+ rtAsn1DumpString(pData, pAsn1Core, "BMP STRING", uDepth);
+ break;
+
+ case ASN1_TAG_UTC_TIME:
+ rtAsn1DumpTime(pData, pAsn1Core, "UTC TIME");
+ break;
+
+ case ASN1_TAG_GENERALIZED_TIME:
+ rtAsn1DumpTime(pData, pAsn1Core, "GENERALIZED TIME");
+ break;
+
+ case ASN1_TAG_CHARACTER_STRING:
+ rtAsn1DumpPrintf(pData, "CHARACTER STRING -- cb=%u TODO\n", pAsn1Core->cb);
+ break;
+
+ default:
+ rtAsn1DumpPrintf(pData, "[UNIVERSAL %u]\n", pAsn1Core->uTag);
+ break;
+ }
+ return fOpen;
+}
+
+
+/** @callback_method_impl{FNRTASN1ENUMCALLBACK} */
+static DECLCALLBACK(int) rtAsn1DumpEnumCallback(PRTASN1CORE pAsn1Core, const char *pszName, uint32_t uDepth, void *pvUser)
+{
+ PRTASN1DUMPDATA pData = (PRTASN1DUMPDATA)pvUser;
+ if (!pAsn1Core->fFlags)
+ return VINF_SUCCESS;
+
+ bool fOpen = false;
+ rtAsn1DumpPrintIdent(pData, uDepth);
+ switch (pAsn1Core->fClass & ASN1_TAGCLASS_MASK)
+ {
+ case ASN1_TAGCLASS_UNIVERSAL:
+ rtAsn1DumpPrintf(pData, "%-16s ", pszName);
+ fOpen = rtAsn1DumpUniversalTypeAndValue(pData, pAsn1Core, uDepth);
+ break;
+
+ case ASN1_TAGCLASS_CONTEXT:
+ if ((pAsn1Core->fRealClass & ASN1_TAGCLASS_MASK) == ASN1_TAGCLASS_UNIVERSAL)
+ {
+ rtAsn1DumpPrintf(pData, "%-16s [%u] ", pszName, pAsn1Core->uTag);
+ fOpen = rtAsn1DumpUniversalTypeAndValue(pData, pAsn1Core, uDepth);
+ }
+ else
+ {
+ rtAsn1DumpPrintf(pData, "%-16s [%u]\n", pszName, pAsn1Core->uTag);
+ fOpen = true;
+ }
+ break;
+
+ case ASN1_TAGCLASS_APPLICATION:
+ if ((pAsn1Core->fRealClass & ASN1_TAGCLASS_MASK) == ASN1_TAGCLASS_UNIVERSAL)
+ {
+ rtAsn1DumpPrintf(pData, "%-16s [APPLICATION %u] ", pszName, pAsn1Core->uTag);
+ fOpen = rtAsn1DumpUniversalTypeAndValue(pData, pAsn1Core, uDepth);
+ }
+ else
+ {
+ rtAsn1DumpPrintf(pData, "%-16s [APPLICATION %u]\n", pszName, pAsn1Core->uTag);
+ fOpen = true;
+ }
+ break;
+
+ case ASN1_TAGCLASS_PRIVATE:
+ if (RTASN1CORE_IS_DUMMY(pAsn1Core))
+ rtAsn1DumpPrintf(pData, "%-16s DUMMY\n", pszName);
+ else
+ {
+ rtAsn1DumpPrintf(pData, "%-16s [PRIVATE %u]\n", pszName, pAsn1Core->uTag);
+ fOpen = true;
+ }
+ break;
+ }
+ /** @todo {} */
+
+ /*
+ * Recurse.
+ */
+ if ( pAsn1Core->pOps
+ && pAsn1Core->pOps->pfnEnum)
+ pAsn1Core->pOps->pfnEnum(pAsn1Core, rtAsn1DumpEnumCallback, uDepth, pData);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1Dump(PCRTASN1CORE pAsn1Core, uint32_t fFlags, uint32_t uLevel, PFNRTDUMPPRINTFV pfnPrintfV, void *pvUser)
+{
+ if ( pAsn1Core->pOps
+ && pAsn1Core->pOps->pfnEnum)
+ {
+ RTASN1DUMPDATA Data;
+ Data.fFlags = fFlags;
+ Data.pfnPrintfV = pfnPrintfV;
+ Data.pvUser = pvUser;
+
+ return pAsn1Core->pOps->pfnEnum((PRTASN1CORE)pAsn1Core, rtAsn1DumpEnumCallback, uLevel, &Data);
+ }
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-efence-allocator.cpp b/src/VBox/Runtime/common/asn1/asn1-efence-allocator.cpp
new file mode 100644
index 00000000..ed122378
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-efence-allocator.cpp
@@ -0,0 +1,215 @@
+/* $Id: asn1-efence-allocator.cpp $ */
+/** @file
+ * IPRT - ASN.1, Electric Fense Allocator.
+ */
+
+/*
+ * 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/mem.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+
+/** @interface_method_impl{RTASN1ALLOCATORVTABLE,pfnFree} */
+static DECLCALLBACK(void) rtAsn1EFenceAllocator_Free(PCRTASN1ALLOCATORVTABLE pThis, PRTASN1ALLOCATION pAllocation, void *pv)
+{
+ RT_NOREF_PV(pThis);
+ RTMemEfFreeNP(pv);
+ pAllocation->cbAllocated = 0;
+}
+
+
+/** @interface_method_impl{RTASN1ALLOCATORVTABLE,pfnAlloc} */
+static DECLCALLBACK(int) rtAsn1EFenceAllocator_Alloc(PCRTASN1ALLOCATORVTABLE pThis, PRTASN1ALLOCATION pAllocation,
+ void **ppv, size_t cb)
+{
+ void *pv = RTMemEfAllocZNP(cb, RTMEM_TAG);
+ if (pv)
+ {
+ *ppv = pv;
+ pAllocation->cbAllocated = (uint32_t)cb;
+ return VINF_SUCCESS;
+ }
+ RT_NOREF_PV(pThis);
+ return VERR_NO_MEMORY;
+}
+
+
+/** @interface_method_impl{RTASN1ALLOCATORVTABLE,pfnRealloc} */
+static DECLCALLBACK(int) rtAsn1EFenceAllocator_Realloc(PCRTASN1ALLOCATORVTABLE pThis, PRTASN1ALLOCATION pAllocation,
+ void *pvOld, void **ppvNew, size_t cbNew)
+{
+ Assert(pvOld);
+ Assert(cbNew);
+ void *pv = RTMemEfReallocNP(pvOld, cbNew, RTMEM_TAG);
+ if (pv)
+ {
+ *ppvNew = pv;
+ pAllocation->cbAllocated = (uint32_t)cbNew;
+ return VINF_SUCCESS;
+ }
+ RT_NOREF_PV(pThis);
+ return VERR_NO_MEMORY;
+}
+
+
+/** @interface_method_impl{RTASN1ALLOCATORVTABLE,pfnFreeArray} */
+static DECLCALLBACK(void) rtAsn1EFenceAllocator_FreeArray(PCRTASN1ALLOCATORVTABLE pThis, PRTASN1ARRAYALLOCATION pAllocation,
+ void **papvArray)
+{
+ RT_NOREF_PV(pThis);
+ Assert(papvArray);
+ Assert(pAllocation->cbEntry);
+ Assert(pAllocation->cEntriesAllocated <= pAllocation->cPointersAllocated);
+
+ uint32_t i = pAllocation->cEntriesAllocated;
+ while (i-- > 0)
+ {
+ RTMemEfFreeNP(papvArray[i]);
+ papvArray[i] = NULL;
+ }
+ RTMemEfFreeNP(papvArray);
+
+ pAllocation->cEntriesAllocated = 0;
+ pAllocation->cPointersAllocated = 0;
+}
+
+
+/** @interface_method_impl{RTASN1ALLOCATORVTABLE,pfnGrowArray} */
+static DECLCALLBACK(int) rtAsn1EFenceAllocator_GrowArray(PCRTASN1ALLOCATORVTABLE pThis, PRTASN1ARRAYALLOCATION pAllocation,
+ void ***ppapvArray, uint32_t cMinEntries)
+{
+ RT_NOREF_PV(pThis);
+ Assert(pAllocation->cbEntry);
+ Assert(pAllocation->cEntriesAllocated <= pAllocation->cPointersAllocated);
+
+ /*
+ * Resize the pointer array.
+ */
+ void **papvArray = *ppapvArray;
+ void *pvPointers = RTMemEfReallocNP(papvArray, cMinEntries * sizeof(void *), RTMEM_TAG);
+ if (pvPointers)
+ {
+ *ppapvArray = papvArray = (void **)pvPointers;
+ if (cMinEntries > pAllocation->cPointersAllocated) /* possible on multiple shrink failures */
+ RT_BZERO(&papvArray[pAllocation->cPointersAllocated],
+ (cMinEntries - pAllocation->cPointersAllocated) * sizeof(void *));
+ else
+ AssertFailed();
+ pAllocation->cPointersAllocated = cMinEntries;
+ }
+ else if (cMinEntries > pAllocation->cPointersAllocated)
+ return VERR_NO_MEMORY;
+ /* else: possible but unlikely */
+
+ /*
+ * Add more entries.
+ */
+ while (pAllocation->cEntriesAllocated < cMinEntries)
+ {
+ void *pv;
+ papvArray[pAllocation->cEntriesAllocated] = pv = RTMemEfAllocZNP(pAllocation->cbEntry, RTMEM_TAG);
+ if (pv)
+ pAllocation->cEntriesAllocated++;
+ else
+ return VERR_NO_MEMORY;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTASN1ALLOCATORVTABLE,pfnShrinkArray} */
+static DECLCALLBACK(void) rtAsn1EFenceAllocator_ShrinkArray(PCRTASN1ALLOCATORVTABLE pThis, PRTASN1ARRAYALLOCATION pAllocation,
+ void ***ppapvArray, uint32_t cNew, uint32_t cCurrent)
+{
+ RT_NOREF_PV(pThis);
+ Assert(pAllocation->cbEntry);
+ Assert(pAllocation->cEntriesAllocated <= pAllocation->cPointersAllocated);
+
+ /*
+ * We always free and resize.
+ */
+ Assert(pAllocation->cEntriesAllocated == cCurrent);
+ Assert(cNew < cCurrent);
+
+ /* Free entries. */
+ void **papvArray = *ppapvArray;
+ while (cCurrent-- > cNew)
+ {
+ RTMemEfFreeNP(papvArray[cCurrent]);
+ papvArray[cCurrent] = NULL;
+ }
+ pAllocation->cEntriesAllocated = cNew;
+
+ /* Try resize pointer array. Failure here is a genuine possibility since the
+ efence code will try allocate a new block. This causes extra fun in the
+ grow method above. */
+ void *pvPointers = RTMemEfReallocNP(papvArray, cNew * sizeof(void *), RTMEM_TAG);
+ if (pvPointers)
+ {
+ *ppapvArray = (void **)pvPointers;
+ pAllocation->cPointersAllocated = cNew;
+ }
+}
+
+
+/** The Electric Fence ASN.1 allocator. */
+RT_DECL_DATA_CONST(RTASN1ALLOCATORVTABLE const) g_RTAsn1EFenceAllocator =
+{
+ rtAsn1EFenceAllocator_Free,
+ rtAsn1EFenceAllocator_Alloc,
+ rtAsn1EFenceAllocator_Realloc,
+ rtAsn1EFenceAllocator_FreeArray,
+ rtAsn1EFenceAllocator_GrowArray,
+ rtAsn1EFenceAllocator_ShrinkArray
+};
+
+#if 0 && defined(IN_RING3) /* for efence testing */
+RT_DECL_DATA_CONST(RTASN1ALLOCATORVTABLE const) g_RTAsn1DefaultAllocator =
+{
+ rtAsn1EFenceAllocator_Free,
+ rtAsn1EFenceAllocator_Alloc,
+ rtAsn1EFenceAllocator_Realloc,
+ rtAsn1EFenceAllocator_FreeArray,
+ rtAsn1EFenceAllocator_GrowArray,
+ rtAsn1EFenceAllocator_ShrinkArray
+};
+#endif
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-encode.cpp b/src/VBox/Runtime/common/asn1/asn1-encode.cpp
new file mode 100644
index 00000000..d3beb090
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-encode.cpp
@@ -0,0 +1,535 @@
+/* $Id: asn1-encode.cpp $ */
+/** @file
+ * IPRT - ASN.1, Encoding.
+ */
+
+/*
+ * 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/assert.h>
+#include <iprt/bignum.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+#include <iprt/formats/asn1.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Argument package for rtAsn1EncodePrepareCallback passed by RTAsn1EncodePrepare.
+ */
+typedef struct RTASN1ENCODEPREPARGS
+{
+ /** The size at this level. */
+ uint32_t cb;
+ /** RTASN1ENCODE_F_XXX. */
+ uint32_t fFlags;
+ /** Pointer to the error info. (optional) */
+ PRTERRINFO pErrInfo;
+} RTASN1ENCODEPREPARGS;
+
+
+/**
+ * Argument package for rtAsn1EncodeWriteCallback passed by RTAsn1EncodeWrite.
+ */
+typedef struct RTASN1ENCODEWRITEARGS
+{
+ /** RTASN1ENCODE_F_XXX. */
+ uint32_t fFlags;
+ /** Pointer to the writer funtion. */
+ PFNRTASN1ENCODEWRITER pfnWriter;
+ /** User argument to the writer function. */
+ void *pvUser;
+ /** Pointer to the error info. (optional) */
+ PRTERRINFO pErrInfo;
+} RTASN1ENCODEWRITEARGS;
+
+/**
+ * Argument package for rtAsn1EncodeToBufferCallback passed by
+ * RTAsn1EncodeToBuffer.
+ */
+typedef struct RTASN1ENCODETOBUFARGS
+{
+ /** The destination buffer position (incremented while writing). */
+ uint8_t *pbDst;
+ /** The size of the destination buffer left (decremented while writing). */
+ size_t cbDst;
+} RTASN1ENCODETOBUFARGS;
+
+
+RTDECL(int) RTAsn1EncodeRecalcHdrSize(PRTASN1CORE pAsn1Core, uint32_t fFlags, PRTERRINFO pErrInfo)
+{
+ AssertReturn((fFlags & RTASN1ENCODE_F_RULE_MASK) == RTASN1ENCODE_F_DER, VERR_INVALID_FLAGS);
+ int rc = VINF_SUCCESS;
+
+ uint8_t cbHdr;
+ if ((pAsn1Core->fFlags & (RTASN1CORE_F_PRESENT | RTASN1CORE_F_DUMMY | RTASN1CORE_F_DEFAULT)) == RTASN1CORE_F_PRESENT)
+ {
+ /*
+ * The minimum header size is two bytes.
+ */
+ cbHdr = 2;
+
+ /*
+ * Add additional bytes for encoding the tag.
+ */
+ uint32_t uTag = pAsn1Core->uTag;
+ if (uTag >= ASN1_TAG_USE_LONG_FORM)
+ {
+ AssertReturn(pAsn1Core->uTag != UINT32_MAX, RTErrInfoSet(pErrInfo, VERR_ASN1_DUMMY_OBJECT, "uTag=UINT32_MAX"));
+ do
+ {
+ cbHdr++;
+ uTag >>= 7;
+ } while (uTag > 0);
+ }
+
+ /*
+ * Add additional bytes for encoding the content length.
+ */
+ uint32_t cb = pAsn1Core->cb;
+ if (cb >= 0x80)
+ {
+ AssertReturn(cb < _1G, RTErrInfoSetF(pErrInfo, VERR_ASN1_TOO_LONG, "cb=%u (%#x)", cb, cb));
+
+ if (cb <= UINT32_C(0xffff))
+ {
+ if (cb <= UINT32_C(0xff))
+ cbHdr += 1;
+ else
+ cbHdr += 2;
+ }
+ else
+ {
+ if (cb <= UINT32_C(0xffffff))
+ cbHdr += 3;
+ else
+ cbHdr += 4;
+ }
+ }
+ }
+ /*
+ * Not present, dummy or otherwise not encoded.
+ */
+ else
+ {
+ cbHdr = 0;
+ if (pAsn1Core->fFlags & RTASN1CORE_F_DEFAULT)
+ rc = VINF_ASN1_NOT_ENCODED;
+ else
+ {
+ Assert(RTASN1CORE_IS_DUMMY(pAsn1Core));
+ Assert(pAsn1Core->pOps && pAsn1Core->pOps->pfnEnum);
+ rc = VINF_SUCCESS;
+ }
+ }
+
+ /*
+ * Update the header length.
+ */
+ pAsn1Core->cbHdr = cbHdr;
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNRTASN1ENUMCALLBACK}
+ */
+static DECLCALLBACK(int) rtAsn1EncodePrepareCallback(PRTASN1CORE pAsn1Core, const char *pszName, uint32_t uDepth, void *pvUser)
+{
+ RTASN1ENCODEPREPARGS *pArgs = (RTASN1ENCODEPREPARGS *)pvUser;
+ RT_NOREF_PV(pszName);
+ if (RTASN1CORE_IS_PRESENT(pAsn1Core))
+ {
+ /*
+ * Depth first, where relevant.
+ */
+ uint32_t const cbSaved = pArgs->cb;
+ if (pAsn1Core->pOps)
+ {
+ /*
+ * Use the encoding preparation method when available.
+ */
+ int rc;
+ if (pAsn1Core->pOps->pfnEncodePrep)
+ rc = pAsn1Core->pOps->pfnEncodePrep(pAsn1Core, pArgs->fFlags, pArgs->pErrInfo);
+ else if (pAsn1Core->pOps->pfnEnum)
+ {
+ /*
+ * Recurse to prepare the child objects (if any).
+ */
+ rc = pAsn1Core->pOps->pfnEnum(pAsn1Core, rtAsn1EncodePrepareCallback, uDepth + 1, pArgs);
+ if (RT_SUCCESS(rc))
+ pAsn1Core->cb = pArgs->cb - cbSaved;
+ }
+ else
+ {
+ /*
+ * Must be a primitive type if DER.
+ */
+ if ( (pAsn1Core->fClass & ASN1_TAGFLAG_CONSTRUCTED)
+ && (pArgs->fFlags & RTASN1ENCODE_F_DER) )
+ return RTErrInfoSetF(pArgs->pErrInfo, VERR_ASN1_EXPECTED_PRIMITIVE,
+ "Expected primitive ASN.1 object: uTag=%#x fClass=%#x cb=%u",
+ RTASN1CORE_GET_TAG(pAsn1Core), pAsn1Core->fClass, pAsn1Core->cb);
+ rc = VINF_SUCCESS;
+ }
+ if (RT_SUCCESS(rc))
+ rc = RTAsn1EncodeRecalcHdrSize(pAsn1Core, pArgs->fFlags, pArgs->pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ else
+ {
+ AssertFailed();
+ pAsn1Core->cb = 0;
+ pAsn1Core->cbHdr = 0;
+ }
+
+ /*
+ * Recalculate the output size, thus far. Dummy objects propagates the
+ * content size, but the header size is zero. Other objects with
+ * header size zero are not encoded and should be omitted entirely.
+ */
+ if (pAsn1Core->cbHdr > 0 || RTASN1CORE_IS_DUMMY(pAsn1Core))
+ pArgs->cb = RTASN1CORE_GET_RAW_ASN1_SIZE(pAsn1Core) + cbSaved;
+ else
+ pArgs->cb = cbSaved;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1EncodePrepare(PRTASN1CORE pRoot, uint32_t fFlags, uint32_t *pcbEncoded, PRTERRINFO pErrInfo)
+{
+ AssertReturn((fFlags & RTASN1ENCODE_F_RULE_MASK) == RTASN1ENCODE_F_DER, VERR_INVALID_FLAGS);
+
+ /*
+ * This is implemented as a recursive enumeration of the ASN.1 object structure.
+ */
+ RTASN1ENCODEPREPARGS Args;
+ Args.cb = 0;
+ Args.fFlags = fFlags;
+ Args.pErrInfo = pErrInfo;
+ int rc = rtAsn1EncodePrepareCallback(pRoot, "root", 0, &Args);
+ if (pcbEncoded)
+ *pcbEncoded = RTASN1CORE_GET_RAW_ASN1_SIZE(pRoot);
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1EncodeWriteHeader(PCRTASN1CORE pAsn1Core, uint32_t fFlags, FNRTASN1ENCODEWRITER pfnWriter, void *pvUser,
+ PRTERRINFO pErrInfo)
+{
+ AssertReturn((fFlags & RTASN1ENCODE_F_RULE_MASK) == RTASN1ENCODE_F_DER, VERR_INVALID_FLAGS);
+
+ if ((pAsn1Core->fFlags & (RTASN1CORE_F_PRESENT | RTASN1CORE_F_DUMMY | RTASN1CORE_F_DEFAULT)) == RTASN1CORE_F_PRESENT)
+ {
+ uint8_t abHdr[16]; /* 2 + max 5 tag + max 4 length = 11 */
+ uint8_t *pbDst = &abHdr[0];
+
+ /*
+ * Encode the tag.
+ */
+ uint32_t uTag = pAsn1Core->uTag;
+ if (uTag < ASN1_TAG_USE_LONG_FORM)
+ *pbDst++ = (uint8_t)uTag | (pAsn1Core->fClass & ~ASN1_TAG_MASK);
+ else
+ {
+ AssertReturn(pAsn1Core->uTag != UINT32_MAX, RTErrInfoSet(pErrInfo, VERR_ASN1_DUMMY_OBJECT, "uTag=UINT32_MAX"));
+
+ /* In the long form, the tag is encoded MSB style with the 8th bit
+ of each byte indicating the whether there are more byte. */
+ *pbDst++ = ASN1_TAG_USE_LONG_FORM | (pAsn1Core->fClass & ~ASN1_TAG_MASK);
+ if (uTag <= UINT32_C(0x7f))
+ *pbDst++ = uTag;
+ else if (uTag <= UINT32_C(0x3fff)) /* 2**(7*2) = 0x4000 (16384) */
+ {
+ *pbDst++ = (uTag >> 7) | 0x80;
+ *pbDst++ = uTag & 0x7f;
+ }
+ else if (uTag <= UINT32_C(0x1fffff)) /* 2**(7*3) = 0x200000 (2097152) */
+ {
+ *pbDst++ = (uTag >> 14) | 0x80;
+ *pbDst++ = ((uTag >> 7) & 0x7f) | 0x80;
+ *pbDst++ = uTag & 0x7f;
+ }
+ else if (uTag <= UINT32_C(0xfffffff)) /* 2**(7*4) = 0x10000000 (268435456) */
+ {
+ *pbDst++ = (uTag >> 21) | 0x80;
+ *pbDst++ = ((uTag >> 14) & 0x7f) | 0x80;
+ *pbDst++ = ((uTag >> 7) & 0x7f) | 0x80;
+ *pbDst++ = uTag & 0x7f;
+ }
+ else
+ {
+ *pbDst++ = (uTag >> 28) | 0x80;
+ *pbDst++ = ((uTag >> 21) & 0x7f) | 0x80;
+ *pbDst++ = ((uTag >> 14) & 0x7f) | 0x80;
+ *pbDst++ = ((uTag >> 7) & 0x7f) | 0x80;
+ *pbDst++ = uTag & 0x7f;
+ }
+ }
+
+ /*
+ * Encode the length.
+ */
+ uint32_t cb = pAsn1Core->cb;
+ if (cb < 0x80)
+ *pbDst++ = (uint8_t)cb;
+ else
+ {
+ AssertReturn(cb < _1G, RTErrInfoSetF(pErrInfo, VERR_ASN1_TOO_LONG, "cb=%u (%#x)", cb, cb));
+
+ if (cb <= UINT32_C(0xffff))
+ {
+ if (cb <= UINT32_C(0xff))
+ {
+ pbDst[0] = 0x81;
+ pbDst[1] = (uint8_t)cb;
+ pbDst += 2;
+ }
+ else
+ {
+ pbDst[0] = 0x82;
+ pbDst[1] = cb >> 8;
+ pbDst[2] = (uint8_t)cb;
+ pbDst += 3;
+ }
+ }
+ else
+ {
+ if (cb <= UINT32_C(0xffffff))
+ {
+ pbDst[0] = 0x83;
+ pbDst[1] = (uint8_t)(cb >> 16);
+ pbDst[2] = (uint8_t)(cb >> 8);
+ pbDst[3] = (uint8_t)cb;
+ pbDst += 4;
+ }
+ else
+ {
+ pbDst[0] = 0x84;
+ pbDst[1] = (uint8_t)(cb >> 24);
+ pbDst[2] = (uint8_t)(cb >> 16);
+ pbDst[3] = (uint8_t)(cb >> 8);
+ pbDst[4] = (uint8_t)cb;
+ pbDst += 5;
+ }
+ }
+ }
+
+ size_t const cbHdr = pbDst - &abHdr[0];
+ Assert(sizeof(abHdr) >= cbHdr);
+ Assert(pAsn1Core->cbHdr == cbHdr);
+
+ /*
+ * Write it.
+ */
+ return pfnWriter(abHdr, cbHdr, pvUser, pErrInfo);
+ }
+
+ /*
+ * Not present, dummy or otherwise not encoded.
+ */
+ Assert(pAsn1Core->cbHdr == 0);
+ if (pAsn1Core->fFlags & RTASN1CORE_F_DEFAULT)
+ return VINF_ASN1_NOT_ENCODED;
+ Assert(RTASN1CORE_IS_DUMMY(pAsn1Core));
+ Assert(pAsn1Core->pOps && pAsn1Core->pOps->pfnEnum);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNRTASN1ENUMCALLBACK}
+ */
+static DECLCALLBACK(int) rtAsn1EncodeWriteCallback(PRTASN1CORE pAsn1Core, const char *pszName, uint32_t uDepth, void *pvUser)
+{
+ RTASN1ENCODEWRITEARGS *pArgs = (RTASN1ENCODEWRITEARGS *)pvUser;
+ RT_NOREF_PV(pszName);
+ int rc;
+ if (RTASN1CORE_IS_PRESENT(pAsn1Core))
+ {
+ /*
+ * If there is an write method, use it.
+ */
+ if ( pAsn1Core->pOps
+ && pAsn1Core->pOps->pfnEncodeWrite)
+ rc = pAsn1Core->pOps->pfnEncodeWrite(pAsn1Core, pArgs->fFlags, pArgs->pfnWriter, pArgs->pvUser, pArgs->pErrInfo);
+ else
+ {
+ /*
+ * Generic path. Start by writing the header for this object.
+ */
+ rc = RTAsn1EncodeWriteHeader(pAsn1Core, pArgs->fFlags, pArgs->pfnWriter, pArgs->pvUser, pArgs->pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * If there is an enum function, call it to assemble the content.
+ * Otherwise ASSUME the pointer in the header points to the content.
+ */
+ if ( pAsn1Core->pOps
+ && pAsn1Core->pOps->pfnEnum)
+ {
+ if (rc != VINF_ASN1_NOT_ENCODED)
+ rc = pAsn1Core->pOps->pfnEnum(pAsn1Core, rtAsn1EncodeWriteCallback, uDepth + 1, pArgs);
+ }
+ else if (pAsn1Core->cb && rc != VINF_ASN1_NOT_ENCODED)
+ {
+ Assert(!RTASN1CORE_IS_DUMMY(pAsn1Core));
+ AssertPtrReturn(pAsn1Core->uData.pv,
+ RTErrInfoSetF(pArgs->pErrInfo, VERR_ASN1_INVALID_DATA_POINTER,
+ "Invalid uData pointer %p for no pfnEnum object with %#x bytes of content",
+ pAsn1Core->uData.pv, pAsn1Core->cb));
+ rc = pArgs->pfnWriter(pAsn1Core->uData.pv, pAsn1Core->cb, pArgs->pvUser, pArgs->pErrInfo);
+ }
+ }
+ }
+ if (RT_SUCCESS(rc))
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VINF_SUCCESS;
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1EncodeWrite(PCRTASN1CORE pRoot, uint32_t fFlags, FNRTASN1ENCODEWRITER pfnWriter, void *pvUser,
+ PRTERRINFO pErrInfo)
+{
+ AssertReturn((fFlags & RTASN1ENCODE_F_RULE_MASK) == RTASN1ENCODE_F_DER, VERR_INVALID_FLAGS);
+
+ /*
+ * This is implemented as a recursive enumeration of the ASN.1 object structure.
+ */
+ RTASN1ENCODEWRITEARGS Args;
+ Args.fFlags = fFlags;
+ Args.pfnWriter = pfnWriter;
+ Args.pvUser = pvUser;
+ Args.pErrInfo = pErrInfo;
+ return rtAsn1EncodeWriteCallback((PRTASN1CORE)pRoot, "root", 0, &Args);
+}
+
+
+static DECLCALLBACK(int) rtAsn1EncodeToBufferCallback(const void *pvBuf, size_t cbToWrite, void *pvUser, PRTERRINFO pErrInfo)
+{
+ RTASN1ENCODETOBUFARGS *pArgs = (RTASN1ENCODETOBUFARGS *)pvUser;
+ if (RT_LIKELY(pArgs->cbDst >= cbToWrite))
+ {
+ memcpy(pArgs->pbDst, pvBuf, cbToWrite);
+ pArgs->cbDst -= cbToWrite;
+ pArgs->pbDst += cbToWrite;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Overflow.
+ */
+ if (pArgs->cbDst)
+ {
+ memcpy(pArgs->pbDst, pvBuf, pArgs->cbDst);
+ pArgs->pbDst -= pArgs->cbDst;
+ pArgs->cbDst = 0;
+ }
+ RT_NOREF_PV(pErrInfo);
+ return VERR_BUFFER_OVERFLOW;
+}
+
+
+RTDECL(int) RTAsn1EncodeToBuffer(PCRTASN1CORE pRoot, uint32_t fFlags, void *pvBuf, size_t cbBuf, PRTERRINFO pErrInfo)
+{
+ RTASN1ENCODETOBUFARGS Args;
+ Args.pbDst = (uint8_t *)pvBuf;
+ Args.cbDst = cbBuf;
+ return RTAsn1EncodeWrite(pRoot, fFlags, rtAsn1EncodeToBufferCallback, &Args, pErrInfo);
+}
+
+
+RTDECL(int) RTAsn1EncodeQueryRawBits(PRTASN1CORE pRoot, const uint8_t **ppbRaw, uint32_t *pcbRaw,
+ void **ppvFree, PRTERRINFO pErrInfo)
+{
+ /*
+ * ASSUME that if we've got pointers here, they are valid...
+ */
+ if ( pRoot->uData.pv
+ && !(pRoot->fFlags & RTASN1CORE_F_INDEFINITE_LENGTH) /* BER, not DER. */
+ && (pRoot->fFlags & RTASN1CORE_F_DECODED_CONTENT) )
+ {
+ /** @todo Check that it's DER encoding. */
+ *ppbRaw = RTASN1CORE_GET_RAW_ASN1_PTR(pRoot);
+ *pcbRaw = RTASN1CORE_GET_RAW_ASN1_SIZE(pRoot);
+ *ppvFree = NULL;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Encode it into a temporary heap buffer.
+ */
+ uint32_t cbEncoded = 0;
+ int rc = RTAsn1EncodePrepare(pRoot, RTASN1ENCODE_F_DER, &cbEncoded, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ void *pvEncoded = RTMemTmpAllocZ(cbEncoded);
+ if (pvEncoded)
+ {
+ rc = RTAsn1EncodeToBuffer(pRoot, RTASN1ENCODE_F_DER, pvEncoded, cbEncoded, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ *ppvFree = pvEncoded;
+ *ppbRaw = (unsigned char *)pvEncoded;
+ *pcbRaw = cbEncoded;
+ return VINF_SUCCESS;
+ }
+ RTMemTmpFree(pvEncoded);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_NO_TMP_MEMORY, "RTMemTmpAllocZ(%u)", cbEncoded);
+ }
+
+ *ppvFree = NULL;
+ *ppbRaw = NULL;
+ *pcbRaw = 0;
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-safer-allocator.cpp b/src/VBox/Runtime/common/asn1/asn1-safer-allocator.cpp
new file mode 100644
index 00000000..602b2faa
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-safer-allocator.cpp
@@ -0,0 +1,232 @@
+/* $Id: asn1-safer-allocator.cpp $ */
+/** @file
+ * IPRT - ASN.1, Safer Allocator, for sensitive data.
+ */
+
+/*
+ * 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/memsafer.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+
+/**
+ * Aligns allocation sizes a little.
+ *
+ * @returns Aligned size.
+ * @param cb Requested size.
+ */
+static size_t rtAsn1SaferAllocator_AlignSize(size_t cb)
+{
+ if (cb >= 64)
+ return RT_ALIGN_Z(cb, 64);
+ if (cb >= 32)
+ return RT_ALIGN_Z(cb, 32);
+ if (cb >= 16)
+ return RT_ALIGN_Z(cb, 16);
+ return cb;
+}
+
+
+/** @interface_method_impl{RTASN1ALLOCATORVTABLE,pfnFree} */
+static DECLCALLBACK(void) rtAsn1SaferAllocator_Free(PCRTASN1ALLOCATORVTABLE pThis, PRTASN1ALLOCATION pAllocation, void *pv)
+{
+ RT_NOREF_PV(pThis);
+ RTMemSaferFree(pv, pAllocation->cbAllocated);
+ pAllocation->cbAllocated = 0;
+}
+
+
+/** @interface_method_impl{RTASN1ALLOCATORVTABLE,pfnAlloc} */
+static DECLCALLBACK(int) rtAsn1SaferAllocator_Alloc(PCRTASN1ALLOCATORVTABLE pThis, PRTASN1ALLOCATION pAllocation,
+ void **ppv, size_t cb)
+{
+ size_t cbAlloc = rtAsn1SaferAllocator_AlignSize(cb);
+ void *pv = RTMemSaferAllocZ(cbAlloc);
+ if (pv)
+ {
+ *ppv = pv;
+ pAllocation->cbAllocated = (uint32_t)cbAlloc;
+ return VINF_SUCCESS;
+ }
+ RT_NOREF_PV(pThis);
+ return VERR_NO_MEMORY;
+}
+
+
+/** @interface_method_impl{RTASN1ALLOCATORVTABLE,pfnRealloc} */
+static DECLCALLBACK(int) rtAsn1SaferAllocator_Realloc(PCRTASN1ALLOCATORVTABLE pThis, PRTASN1ALLOCATION pAllocation,
+ void *pvOld, void **ppvNew, size_t cbNew)
+{
+ Assert(pvOld);
+ Assert(cbNew);
+ size_t cbAlloc = rtAsn1SaferAllocator_AlignSize(cbNew);
+ void *pv = RTMemSaferReallocZ(pAllocation->cbAllocated, pvOld, cbAlloc);
+ if (pv)
+ {
+ *ppvNew = pv;
+ pAllocation->cbAllocated = (uint32_t)cbAlloc;
+ return VINF_SUCCESS;
+ }
+ RT_NOREF_PV(pThis);
+ return VERR_NO_MEMORY;
+}
+
+
+/** @interface_method_impl{RTASN1ALLOCATORVTABLE,pfnFreeArray} */
+static DECLCALLBACK(void) rtAsn1SaferAllocator_FreeArray(PCRTASN1ALLOCATORVTABLE pThis, PRTASN1ARRAYALLOCATION pAllocation,
+ void **papvArray)
+{
+ RT_NOREF_PV(pThis);
+ Assert(papvArray);
+ Assert(pAllocation->cbEntry);
+
+ uint32_t i = pAllocation->cEntriesAllocated;
+ while (i-- > 0)
+ RTMemSaferFree(papvArray[i], pAllocation->cbEntry);
+ RTMemFree(papvArray);
+
+ pAllocation->cEntriesAllocated = 0;
+ pAllocation->cPointersAllocated = 0;
+}
+
+
+/** @interface_method_impl{RTASN1ALLOCATORVTABLE,pfnGrowArray} */
+static DECLCALLBACK(int) rtAsn1SaferAllocator_GrowArray(PCRTASN1ALLOCATORVTABLE pThis, PRTASN1ARRAYALLOCATION pAllocation,
+ void ***ppapvArray, uint32_t cMinEntries)
+{
+ RT_NOREF_PV(pThis);
+
+ /*
+ * Resize the pointer array. We do chunks of 64 bytes for now.
+ */
+ void **papvArray = *ppapvArray;
+ uint32_t cPointers = RT_ALIGN_32(cMinEntries, 64 / sizeof(void *));
+ if (cPointers > pAllocation->cPointersAllocated)
+ {
+ void *pvPointers = RTMemRealloc(papvArray, cPointers * sizeof(void *));
+ if (pvPointers)
+ { /* likely */ }
+ else if (cMinEntries > pAllocation->cPointersAllocated)
+ {
+ cPointers = cMinEntries;
+ pvPointers = RTMemRealloc(*ppapvArray, cPointers * sizeof(void *));
+ if (!pvPointers)
+ return VERR_NO_MEMORY;
+ }
+ else
+ {
+ cPointers = pAllocation->cPointersAllocated;
+ pvPointers = papvArray;
+ }
+
+ *ppapvArray = papvArray = (void **)pvPointers;
+ RT_BZERO(&papvArray[pAllocation->cPointersAllocated], (cPointers - pAllocation->cPointersAllocated) * sizeof(void *));
+ pAllocation->cPointersAllocated = cPointers;
+ }
+
+ /*
+ * Add more entries. Do multiple as the array grows.
+ *
+ * Note! We could possibly optimize this by allocating slabs of entries and
+ * slice them up. However, keep things as simple as possible for now.
+ */
+ uint32_t cEntries = cMinEntries;
+ if (cEntries > 2)
+ {
+ if (cEntries > 8)
+ cEntries = RT_ALIGN_32(cEntries, 4);
+ else
+ cEntries = RT_ALIGN_32(cEntries, 2);
+ cEntries = RT_MIN(cEntries, cPointers);
+ Assert(cEntries >= cMinEntries);
+ }
+ Assert(cEntries <= pAllocation->cPointersAllocated);
+
+ while (pAllocation->cEntriesAllocated < cEntries)
+ {
+ void *pv;
+ papvArray[pAllocation->cEntriesAllocated] = pv = RTMemSaferAllocZ(pAllocation->cbEntry);
+ if (pv)
+ pAllocation->cEntriesAllocated++;
+ else if (pAllocation->cEntriesAllocated >= cMinEntries)
+ break;
+ else
+ return VERR_NO_MEMORY;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTASN1ALLOCATORVTABLE,pfnShrinkArray} */
+static DECLCALLBACK(void) rtAsn1SaferAllocator_ShrinkArray(PCRTASN1ALLOCATORVTABLE pThis, PRTASN1ARRAYALLOCATION pAllocation,
+ void ***ppapvArray, uint32_t cNew, uint32_t cCurrent)
+{
+ RT_NOREF_PV(pThis);
+
+ /*
+ * For now we only zero the entries being removed.
+ */
+ void **papvArray = *ppapvArray;
+ while (cNew < cCurrent)
+ {
+ RTMemWipeThoroughly(papvArray[cNew], pAllocation->cbEntry, 3);
+ RT_BZERO(papvArray[cNew], pAllocation->cbEntry);
+ cNew++;
+ }
+}
+
+
+
+/** The Safer ASN.1 allocator. */
+#if 1 || !defined(IN_RING3) || defined(DOXYGEN_RUNNING)
+RT_DECL_DATA_CONST(RTASN1ALLOCATORVTABLE const) g_RTAsn1SaferAllocator =
+#else
+RT_DECL_DATA_CONST(RTASN1ALLOCATORVTABLE const) g_RTAsn1SaferAllocatorDisabled =
+#endif
+{
+ rtAsn1SaferAllocator_Free,
+ rtAsn1SaferAllocator_Alloc,
+ rtAsn1SaferAllocator_Realloc,
+ rtAsn1SaferAllocator_FreeArray,
+ rtAsn1SaferAllocator_GrowArray,
+ rtAsn1SaferAllocator_ShrinkArray
+};
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-bitstring-decode.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-bitstring-decode.cpp
new file mode 100644
index 00000000..994173f1
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-bitstring-decode.cpp
@@ -0,0 +1,141 @@
+/* $Id: asn1-ut-bitstring-decode.cpp $ */
+/** @file
+ * IPRT - ASN.1, BIT STRING Type, Decoding.
+ */
+
+/*
+ * 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/err.h>
+#include <iprt/string.h>
+
+#include <iprt/formats/asn1.h>
+
+
+
+RTDECL(int) RTAsn1BitString_DecodeAsn1Ex(PRTASN1CURSOR pCursor, uint32_t fFlags, uint32_t cMaxBits, PRTASN1BITSTRING pThis,
+ const char *pszErrorTag)
+{
+ pThis->cBits = 0;
+ pThis->cMaxBits = cMaxBits;
+ pThis->uBits.pv = NULL;
+ pThis->pEncapsulated = NULL;
+ RTAsn1CursorInitAllocation(pCursor, &pThis->EncapsulatedAllocation);
+
+ int rc = RTAsn1CursorReadHdr(pCursor, &pThis->Asn1Core, pszErrorTag);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTAsn1CursorMatchTagClassFlagsString(pCursor, &pThis->Asn1Core, ASN1_TAG_BIT_STRING,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ fFlags, pszErrorTag, "BIT STRING");
+ if (RT_SUCCESS(rc))
+ {
+ if (!(pThis->Asn1Core.fClass & ASN1_TAGFLAG_CONSTRUCTED))
+ {
+ if ( ( cMaxBits == UINT32_MAX
+ || RT_ALIGN(cMaxBits, 8) / 8 + 1 >= pThis->Asn1Core.cb)
+ && pThis->Asn1Core.cb > 0)
+ {
+ uint8_t cUnusedBits = pThis->Asn1Core.cb > 0 ? *pThis->Asn1Core.uData.pu8 : 0;
+ if (pThis->Asn1Core.cb < 2)
+ {
+ /* Not bits present. */
+ if (cUnusedBits == 0)
+ {
+ pThis->cBits = 0;
+ pThis->uBits.pv = NULL;
+ RTAsn1CursorSkip(pCursor, pThis->Asn1Core.cb);
+ pThis->Asn1Core.pOps = &g_RTAsn1BitString_Vtable;
+ pThis->Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
+ return VINF_SUCCESS;
+ }
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_BITSTRING_ENCODING,
+ "%s: Bad unused bit count: %#x (cb=%#x)",
+ pszErrorTag, cUnusedBits, pThis->Asn1Core.cb);
+ }
+ else if (cUnusedBits < 8)
+ {
+ pThis->cBits = (pThis->Asn1Core.cb - 1) * 8;
+ pThis->cBits -= cUnusedBits;
+ pThis->uBits.pu8 = pThis->Asn1Core.uData.pu8 + 1;
+ if ( !(pCursor->fFlags & (RTASN1CURSOR_FLAGS_DER | RTASN1CURSOR_FLAGS_CER))
+ || cUnusedBits == 0
+ || !( pThis->uBits.pu8[pThis->Asn1Core.cb - 2] & (((uint8_t)1 << cUnusedBits) - (uint8_t)1) ) )
+ {
+ RTAsn1CursorSkip(pCursor, pThis->Asn1Core.cb);
+ pThis->Asn1Core.pOps = &g_RTAsn1BitString_Vtable;
+ pThis->Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
+ return VINF_SUCCESS;
+ }
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_BITSTRING_ENCODING,
+ "%s: Unused bits shall be zero in DER/CER mode: last byte=%#x cUnused=%#x",
+ pszErrorTag, pThis->uBits.pu8[pThis->cBits / 8], cUnusedBits);
+ }
+ else
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_BITSTRING_ENCODING,
+ "%s: Bad unused bit count: %#x (cb=%#x)",
+ pszErrorTag, cUnusedBits, pThis->Asn1Core.cb);
+ }
+ else
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_BITSTRING_ENCODING,
+ "%s: Size mismatch: cb=%#x, expected %#x (cMaxBits=%#x)",
+ pszErrorTag, pThis->Asn1Core.cb, RT_ALIGN(cMaxBits, 8) / 8 + 1, cMaxBits);
+ }
+ else
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CONSTRUCTED_STRING_NOT_IMPL,
+ "%s: Constructed BIT STRING not implemented.", pszErrorTag);
+ }
+ }
+ RT_ZERO(*pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1BitString_DecodeAsn1(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1BITSTRING pThis, const char *pszErrorTag)
+{
+ return RTAsn1BitString_DecodeAsn1Ex(pCursor, fFlags, UINT32_MAX, pThis, pszErrorTag);
+}
+
+
+/*
+ * Generate code for the associated collection types.
+ */
+#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-bitstring-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+#include <iprt/asn1-generator-asn1-decoder.h>
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-bitstring-template.h b/src/VBox/Runtime/common/asn1/asn1-ut-bitstring-template.h
new file mode 100644
index 00000000..17375f86
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-bitstring-template.h
@@ -0,0 +1,55 @@
+/* $Id: asn1-ut-bitstring-template.h $ */
+/** @file
+ * IPRT - ASN.1, Bit String Type, Collection Type Template.
+ */
+
+/*
+ * 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
+ */
+
+#define RTASN1TMPL_DECL RTDECL
+
+#define RTASN1TMPL_TYPE RTASN1SEQOFBITSTRINGS
+#define RTASN1TMPL_EXT_NAME RTAsn1SeqOfBitStrings
+#define RTASN1TMPL_INT_NAME rtAsn1SeqOfBitStrings
+RTASN1TMPL_SEQ_OF(RTASN1BITSTRING, RTAsn1BitString);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+#define RTASN1TMPL_TYPE RTASN1SETOFBITSTRINGS
+#define RTASN1TMPL_EXT_NAME RTAsn1SetOfBitStrings
+#define RTASN1TMPL_INT_NAME rtAsn1SetOfBitStrings
+RTASN1TMPL_SET_OF(RTASN1BITSTRING, RTAsn1BitString);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-bitstring.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-bitstring.cpp
new file mode 100644
index 00000000..80499ab5
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-bitstring.cpp
@@ -0,0 +1,548 @@
+/* $Id: asn1-ut-bitstring.cpp $ */
+/** @file
+ * IPRT - ASN.1, Bit String Type.
+ *
+ * @remarks This file should remain very similar to asn1-ut-octetstring.cpp.
+ */
+
+/*
+ * 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 *
+*********************************************************************************************************************************/
+typedef struct RTASN1BITSTRINGWRITERCTX
+{
+ /** Pointer to the output buffer. */
+ uint8_t *pbBuf;
+ /** The current buffer offset. */
+ uint32_t offBuf;
+ /** The size of the buffer. */
+ uint32_t cbBuf;
+} RTASN1BITSTRINGWRITERCTX;
+
+
+/** @callback_method_impl{FNRTASN1ENCODEWRITER,
+ * Used to refresh the content of octet and bit strings. } */
+static DECLCALLBACK(int) rtAsn1BitStringEncodeWriter(const void *pvBuf, size_t cbToWrite, void *pvUser, PRTERRINFO pErrInfo)
+{
+ RTASN1BITSTRINGWRITERCTX *pCtx = (RTASN1BITSTRINGWRITERCTX *)pvUser;
+ AssertReturn(cbToWrite <= pCtx->cbBuf - pCtx->offBuf,
+ RTErrInfoSetF(pErrInfo, VERR_BUFFER_OVERFLOW,
+ "cbToWrite=%#x offBuf=%#x cbBuf=%#x", cbToWrite, pCtx->cbBuf, pCtx->offBuf));
+ memcpy(&pCtx->pbBuf[pCtx->offBuf], pvBuf, cbToWrite);
+ pCtx->offBuf += (uint32_t)cbToWrite;
+ return VINF_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNRTASN1ENCODEWRITER,
+ * Used to compare the encoded raw content of an octet or bit string with the
+ * encapsulated object. } */
+static DECLCALLBACK(int) rtAsn1BitStringEncodeCompare(const void *pvBuf, size_t cbToWrite, void *pvUser, PRTERRINFO pErrInfo)
+{
+ RTASN1BITSTRINGWRITERCTX *pCtx = (RTASN1BITSTRINGWRITERCTX *)pvUser;
+ AssertReturn(cbToWrite <= pCtx->cbBuf - pCtx->offBuf, VERR_BUFFER_OVERFLOW);
+ if (memcmp(&pCtx->pbBuf[pCtx->offBuf], pvBuf, cbToWrite) != 0)
+ return VERR_NOT_EQUAL;
+ pCtx->offBuf += (uint32_t)cbToWrite;
+ RT_NOREF_PV(pErrInfo);
+ return VINF_SUCCESS;
+}
+
+
+
+/*
+ * ASN.1 BIT STRING - Special Methods.
+ */
+
+RTDECL(uint64_t) RTAsn1BitString_GetAsUInt64(PCRTASN1BITSTRING pThis)
+{
+ /*
+ * Extract the first 64 bits in host order.
+ */
+ uint8_t const *pb = pThis->uBits.pu8;
+ uint64_t uRet = 0;
+ uint32_t cShift = 0;
+ uint32_t cBits = RT_MIN(pThis->cBits, 64);
+ while (cBits > 0)
+ {
+ uint8_t b = *pb++;
+#if 1 /* We don't have a bit-order constant... */
+ b = ((b & 0x01) << 7)
+ | ((b & 0x02) << 5)
+ | ((b & 0x04) << 3)
+ | ((b & 0x08) << 1)
+ | ((b & 0x10) >> 1)
+ | ((b & 0x20) >> 3)
+ | ((b & 0x40) >> 5)
+ | ((b & 0x80) >> 7);
+#endif
+ if (cBits < 8)
+ {
+ b &= RT_BIT_32(cBits) - 1;
+ uRet |= (uint64_t)b << cShift;
+ break;
+ }
+ uRet |= (uint64_t)b << cShift;
+ cShift += 8;
+ cBits -= 8;
+ }
+
+ return uRet;
+}
+
+
+RTDECL(int) RTAsn1BitString_RefreshContent(PRTASN1BITSTRING pThis, uint32_t fFlags,
+ PCRTASN1ALLOCATORVTABLE pAllocator, PRTERRINFO pErrInfo)
+{
+ AssertReturn(pThis->pEncapsulated, VERR_INVALID_STATE);
+
+ uint32_t cbEncoded;
+ int rc = RTAsn1EncodePrepare(pThis->pEncapsulated, fFlags, &cbEncoded, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->Asn1Core.cb = 1 + cbEncoded;
+ pThis->cBits = cbEncoded * 8;
+ AssertReturn(pThis->cBits / 8 == cbEncoded, RTErrInfoSetF(pErrInfo, VERR_TOO_MUCH_DATA, "cbEncoded=%#x", cbEncoded));
+
+ rc = RTAsn1ContentReallocZ(&pThis->Asn1Core, cbEncoded + 1, pAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->uBits.pu8 = pThis->Asn1Core.uData.pu8 + 1;
+
+ /* Initialize the writer context and write the first byte concerning unused bits. */
+ RTASN1BITSTRINGWRITERCTX Ctx;
+ Ctx.pbBuf = (uint8_t *)pThis->Asn1Core.uData.pu8;
+ Ctx.cbBuf = cbEncoded + 1;
+ Ctx.offBuf = 1;
+ *Ctx.pbBuf = 0;
+
+ rc = RTAsn1EncodeWrite(pThis->pEncapsulated, fFlags, rtAsn1BitStringEncodeWriter, &Ctx, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ if (Ctx.offBuf == cbEncoded + 1)
+ return VINF_SUCCESS;
+
+ rc = RTErrInfoSetF(pErrInfo, rc, "Expected %#x + 1 bytes, got %#x", cbEncoded, Ctx.offBuf);
+ }
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "Error allocating %#x + 1 bytes for storing content\n", cbEncoded);
+ }
+ return rc;
+}
+
+
+RTDECL(bool) RTAsn1BitString_AreContentBitsValid(PCRTASN1BITSTRING pThis, uint32_t fFlags)
+{
+ if (pThis->pEncapsulated)
+ {
+ if (pThis->cBits & 7)
+ return false;
+
+ /* Check the encoded length of the bits. */
+ uint32_t cbEncoded;
+ int rc = RTAsn1EncodePrepare(pThis->pEncapsulated, fFlags, &cbEncoded, NULL);
+ if (RT_FAILURE(rc))
+ return false;
+ if (pThis->Asn1Core.cb != 1 + cbEncoded)
+ return false;
+
+ /* Check the encoded bits, if there are any. */
+ if (cbEncoded)
+ {
+ if (!pThis->Asn1Core.uData.pv)
+ return false;
+
+ /* Check the first byte, the unused bit count. */
+ if (*pThis->Asn1Core.uData.pu8 != 0)
+ return false;
+
+ /* Check the other bytes. */
+ RTASN1BITSTRINGWRITERCTX Ctx;
+ Ctx.pbBuf = (uint8_t *)pThis->Asn1Core.uData.pu8;
+ Ctx.cbBuf = cbEncoded + 1;
+ Ctx.offBuf = 1;
+ rc = RTAsn1EncodeWrite(pThis->pEncapsulated, fFlags, rtAsn1BitStringEncodeCompare, &Ctx, NULL);
+ if (RT_FAILURE(rc))
+ return false;
+ }
+ }
+ return true;
+}
+
+
+
+
+/*
+ * ASN.1 BIT STRING - Standard Methods.
+ */
+
+/** @interface_method_impl{FNRTASN1COREVTENCODEPREP} */
+static DECLCALLBACK(int) RTAsn1BitString_EncodePrep(PRTASN1CORE pThisCore, uint32_t fFlags, PRTERRINFO pErrInfo)
+{
+ PRTASN1BITSTRING pThis = (PRTASN1BITSTRING)pThisCore;
+ if (!pThis->pEncapsulated)
+ {
+ Assert(pThis->cBits == 0 || pThis->Asn1Core.uData.pv);
+ return VINF_SUCCESS;
+ }
+
+ /* Figure out the size of the encapsulated content. */
+ uint32_t cbEncoded;
+ int rc = RTAsn1EncodePrepare(pThis->pEncapsulated, fFlags, &cbEncoded, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /* Free the bytes if they don't match up. */
+ if (pThis->Asn1Core.uData.pv)
+ {
+ bool fMustFree = pThis->Asn1Core.cb != 1 + cbEncoded || (pThis->cBits & 7);
+ if (!fMustFree)
+ {
+ RTASN1BITSTRINGWRITERCTX Ctx;
+ Ctx.pbBuf = (uint8_t *)pThis->Asn1Core.uData.pu8;
+ Ctx.cbBuf = 1 + cbEncoded;
+ Ctx.offBuf = 1;
+ fMustFree = *Ctx.pbBuf != 0;
+ if (!fMustFree)
+ {
+ rc = RTAsn1EncodeWrite(pThis->pEncapsulated, fFlags, rtAsn1BitStringEncodeCompare, &Ctx, NULL);
+ fMustFree = RT_FAILURE_NP(rc);
+ }
+ }
+ if (fMustFree)
+ {
+ pThis->uBits.pv = NULL;
+ RTAsn1ContentFree(&pThis->Asn1Core);
+ }
+ }
+ pThis->Asn1Core.cb = 1 + cbEncoded;
+ pThis->cBits = cbEncoded * 8;
+
+ rc = RTAsn1EncodeRecalcHdrSize(&pThis->Asn1Core, fFlags, pErrInfo);
+ }
+ return rc;
+}
+
+
+/** @interface_method_impl{FNRTASN1COREVTENCODEWRITE} */
+static DECLCALLBACK(int) RTAsn1BitString_EncodeWrite(PRTASN1CORE pThisCore, uint32_t fFlags, PFNRTASN1ENCODEWRITER pfnWriter,
+ void *pvUser, PRTERRINFO pErrInfo)
+{
+ PRTASN1BITSTRING pThis = (PRTASN1BITSTRING)pThisCore;
+
+ AssertReturn(RT_ALIGN(pThis->cBits, 8) / 8 + 1 == pThis->Asn1Core.cb, VERR_INTERNAL_ERROR_3);
+
+ /*
+ * First the header.
+ */
+ int rc = RTAsn1EncodeWriteHeader(&pThis->Asn1Core, fFlags, pfnWriter, pvUser, pErrInfo);
+ if (RT_SUCCESS(rc) && rc != VINF_ASN1_NOT_ENCODED)
+ {
+ /*
+ * The content starts with an unused bit count. Calculate it in case we
+ * need to write it out.
+ */
+ uint8_t cUnusedBits = 0;
+ if ((pThis->cBits & 7) != 0)
+ cUnusedBits = 8 - (pThis->cBits & 7);
+
+ /*
+ * If nothing is encapsulated, the core points to the content (if we have any).
+ */
+ if (!pThis->pEncapsulated)
+ {
+ if (pThis->cBits > 0)
+ {
+ Assert(pThis->Asn1Core.uData.pu8[0] == cUnusedBits);
+ rc = pfnWriter(pThis->Asn1Core.uData.pu8, pThis->Asn1Core.cb, pvUser, pErrInfo);
+ }
+ else
+ rc = pfnWriter(&cUnusedBits, sizeof(cUnusedBits), pvUser, pErrInfo);
+ }
+ /*
+ * Write the unused bit count and then call upon the encapsulated
+ * content to serialize itself.
+ */
+ else
+ {
+ rc = pfnWriter(&cUnusedBits, sizeof(cUnusedBits), pvUser, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = RTAsn1EncodeWrite(pThis->pEncapsulated, fFlags, pfnWriter, pvUser, pErrInfo);
+ }
+ }
+ return rc;
+}
+
+
+RT_DECL_DATA_CONST(RTASN1COREVTABLE const) g_RTAsn1BitString_Vtable =
+{
+ "RTAsn1BitString",
+ sizeof(RTASN1BITSTRING),
+ ASN1_TAG_BIT_STRING,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ 0,
+ (PFNRTASN1COREVTDTOR)RTAsn1BitString_Delete,
+ (PFNRTASN1COREVTENUM)RTAsn1BitString_Enum,
+ (PFNRTASN1COREVTCLONE)RTAsn1BitString_Clone,
+ (PFNRTASN1COREVTCOMPARE)RTAsn1BitString_Compare,
+ (PFNRTASN1COREVTCHECKSANITY)RTAsn1BitString_CheckSanity,
+ RTAsn1BitString_EncodePrep,
+ RTAsn1BitString_EncodeWrite
+};
+
+
+RTDECL(int) RTAsn1BitString_Init(PRTASN1BITSTRING pThis, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ RT_ZERO(*pThis);
+
+ RTAsn1Core_InitEx(&pThis->Asn1Core, ASN1_TAG_BIT_STRING, ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ &g_RTAsn1BitString_Vtable, RTASN1CORE_F_PRESENT | RTASN1CORE_F_PRIMITE_TAG_STRUCT);
+ /*pThis->cBits = 0;
+ pThis->cMaxBits = 0;
+ pThis->uBits.pv = NULL;
+ pThis->pEncapsulated = NULL; */
+ RTAsn1MemInitAllocation(&pThis->EncapsulatedAllocation, pAllocator);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1BitString_InitWithData(PRTASN1BITSTRING pThis, void const *pvSrc, uint32_t cSrcBits,
+ PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ RTAsn1BitString_Init(pThis, pAllocator);
+ Assert(pThis->pEncapsulated == NULL);
+
+ uint32_t cbToCopy = (cSrcBits + 7) / 8;
+ int rc = RTAsn1ContentAllocZ(&pThis->Asn1Core, cbToCopy + 1, pAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->cBits = cSrcBits;
+ uint8_t *pbDst = (uint8_t *)pThis->Asn1Core.uData.pu8;
+ pThis->uBits.pv = pbDst + 1;
+ *pbDst = (cSrcBits & 7) != 0 ? 8 - (cSrcBits & 7) : 0; /* unused bits */
+ memcpy(pbDst + 1, pvSrc, cbToCopy);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1BitString_Clone(PRTASN1BITSTRING pThis, PCRTASN1BITSTRING pSrc, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ AssertPtr(pSrc); AssertPtr(pThis); AssertPtr(pAllocator);
+
+ RT_ZERO(*pThis);
+ if (RTAsn1BitString_IsPresent(pSrc))
+ {
+ AssertReturn(pSrc->Asn1Core.pOps == &g_RTAsn1BitString_Vtable, VERR_INTERNAL_ERROR_3);
+
+ int rc;
+ if (!pSrc->pEncapsulated)
+ rc = RTAsn1Core_CloneContent(&pThis->Asn1Core, &pSrc->Asn1Core, pAllocator);
+ else
+ rc = RTAsn1Core_CloneNoContent(&pThis->Asn1Core, &pSrc->Asn1Core);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ RTAsn1MemInitAllocation(&pThis->EncapsulatedAllocation, pAllocator);
+ pThis->cBits = pSrc->cBits;
+ pThis->cMaxBits = pSrc->cMaxBits;
+ if (!pSrc->pEncapsulated)
+ pThis->uBits.pv = pThis->Asn1Core.uData.pu8 ? pThis->Asn1Core.uData.pu8 + 1 : NULL;
+ else
+ {
+ PCRTASN1COREVTABLE pOps = pSrc->pEncapsulated->pOps;
+ Assert(!pOps || pOps->pfnClone);
+ if (pOps && pOps->pfnClone)
+ {
+ /* We can clone the decoded encapsulated object. */
+ rc = RTAsn1MemAllocZ(&pThis->EncapsulatedAllocation, (void **)&pThis->pEncapsulated, pOps->cbStruct);
+ if (RT_SUCCESS(rc))
+ {
+ rc = pOps->pfnClone(pThis->pEncapsulated, pSrc->pEncapsulated, pAllocator);
+ if (RT_FAILURE(rc))
+ RTAsn1MemFree(&pThis->EncapsulatedAllocation, pThis->pEncapsulated);
+ }
+ }
+ else
+ {
+ /* Borrow the encapsulated pointer and use RTAsn1BitString_RefreshContent
+ to get an accurate copy of the bytes. */
+ pThis->pEncapsulated = pSrc->pEncapsulated;
+ rc = RTAsn1BitString_RefreshContent(pThis, RTASN1ENCODE_F_DER, pAllocator, NULL);
+ pThis->pEncapsulated = NULL;
+ }
+ if (RT_FAILURE(rc))
+ {
+ RTAsn1ContentFree(&pThis->Asn1Core);
+ RT_ZERO(*pThis);
+ return rc;
+ }
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(void) RTAsn1BitString_Delete(PRTASN1BITSTRING pThis)
+{
+ if ( pThis
+ && RTAsn1BitString_IsPresent(pThis))
+ {
+ Assert(pThis->Asn1Core.pOps == &g_RTAsn1BitString_Vtable);
+
+ /* Destroy the encapsulated object. */
+ if (pThis->pEncapsulated)
+ {
+ RTAsn1VtDelete(pThis->pEncapsulated);
+ if (pThis->EncapsulatedAllocation.cbAllocated)
+ RTAsn1MemFree(&pThis->EncapsulatedAllocation, pThis->pEncapsulated);
+ }
+
+ /* Delete content and wipe the content. */
+ RTAsn1ContentFree(&pThis->Asn1Core);
+ RT_ZERO(*pThis);
+ }
+}
+
+
+RTDECL(int) RTAsn1BitString_Enum(PRTASN1BITSTRING pThis, PFNRTASN1ENUMCALLBACK pfnCallback, uint32_t uDepth, void *pvUser)
+{
+ Assert(pThis && (!RTAsn1BitString_IsPresent(pThis) || pThis->Asn1Core.pOps == &g_RTAsn1BitString_Vtable));
+
+ /* Enumerate the encapsulated object if present. */
+ if (pThis->pEncapsulated)
+ return pfnCallback(pThis->pEncapsulated, "Encapsulated", uDepth + 1, pvUser);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1BitString_Compare(PCRTASN1BITSTRING pLeft, PCRTASN1BITSTRING pRight)
+{
+ Assert(pLeft && (!RTAsn1BitString_IsPresent(pLeft) || pLeft->Asn1Core.pOps == &g_RTAsn1BitString_Vtable));
+ Assert(pRight && (!RTAsn1BitString_IsPresent(pRight) || pRight->Asn1Core.pOps == &g_RTAsn1BitString_Vtable));
+
+ int iDiff;
+ if (RTAsn1BitString_IsPresent(pLeft))
+ {
+ if (RTAsn1BitString_IsPresent(pRight))
+ {
+ /* Since it's really hard to tell whether encapsulated objects have
+ been modified or not, we might have to refresh both objects
+ while doing this compare. We'll try our best to avoid it though. */
+ if (pLeft->pEncapsulated || pRight->pEncapsulated)
+ {
+ if ( pLeft->pEncapsulated
+ && pRight->pEncapsulated
+ && pLeft->pEncapsulated->pOps == pRight->pEncapsulated->pOps)
+ iDiff = pLeft->pEncapsulated->pOps->pfnCompare(pLeft->pEncapsulated, pRight->pEncapsulated);
+ else
+ {
+ /* No direct comparison of encapsulated objects possible,
+ make sure we've got the rigth bytes then. */
+ if ( pLeft->pEncapsulated
+ && !RTAsn1BitString_AreContentBitsValid(pLeft, RTASN1ENCODE_F_DER))
+ {
+ int rc = RTAsn1BitString_RefreshContent((PRTASN1BITSTRING)pLeft, RTASN1ENCODE_F_DER,
+ pLeft->EncapsulatedAllocation.pAllocator, NULL);
+ AssertRC(rc);
+ }
+
+ if ( pRight->pEncapsulated
+ && !RTAsn1BitString_AreContentBitsValid(pRight, RTASN1ENCODE_F_DER))
+ {
+ int rc = RTAsn1BitString_RefreshContent((PRTASN1BITSTRING)pRight, RTASN1ENCODE_F_DER,
+ pRight->EncapsulatedAllocation.pAllocator, NULL);
+ AssertRC(rc);
+ }
+
+ /* Compare the content bytes. */
+ iDiff = RTAsn1Core_CompareEx(&pLeft->Asn1Core, &pRight->Asn1Core, true /*fIgnoreTagAndClass*/);
+ }
+ }
+ /*
+ * No encapsulated object, just compare the raw content bytes.
+ */
+ else
+ iDiff = RTAsn1Core_CompareEx(&pLeft->Asn1Core, &pRight->Asn1Core, true /*fIgnoreTagAndClass*/);
+ }
+ else
+ iDiff = -1;
+ }
+ else
+ iDiff = 0 - (int)RTAsn1BitString_IsPresent(pRight);
+ return iDiff;
+}
+
+
+RTDECL(int) RTAsn1BitString_CheckSanity(PCRTASN1BITSTRING pThis, uint32_t fFlags, PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ if (RT_UNLIKELY(!RTAsn1BitString_IsPresent(pThis)))
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_NOT_PRESENT, "%s: Missing (BIT STRING).", pszErrorTag);
+
+ if (pThis->cBits > pThis->cMaxBits)
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_BITSTRING_OUT_OF_BOUNDS, "%s: Exceeding max bits: cBits=%u cMaxBits=%u.",
+ pszErrorTag, pThis->cBits, pThis->cMaxBits);
+
+ if (pThis->pEncapsulated)
+ return pThis->pEncapsulated->pOps->pfnCheckSanity(pThis->pEncapsulated, fFlags & RTASN1_CHECK_SANITY_F_COMMON_MASK,
+ pErrInfo, pszErrorTag);
+ return VINF_SUCCESS;
+}
+
+/*
+ * Generate code for the associated collection types.
+ */
+#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-bitstring-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+#include <iprt/asn1-generator-core.h>
+#include <iprt/asn1-generator-init.h>
+#include <iprt/asn1-generator-sanity.h>
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-boolean-decode.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-boolean-decode.cpp
new file mode 100644
index 00000000..a8db498f
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-boolean-decode.cpp
@@ -0,0 +1,93 @@
+/* $Id: asn1-ut-boolean-decode.cpp $ */
+/** @file
+ * IPRT - ASN.1, BOOLEAN Type, Decoding.
+ */
+
+/*
+ * 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/err.h>
+#include <iprt/string.h>
+
+#include <iprt/formats/asn1.h>
+
+
+
+RTDECL(int) RTAsn1Boolean_DecodeAsn1(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1BOOLEAN pThis, const char *pszErrorTag)
+{
+ pThis->fValue = 0;
+ int rc = RTAsn1CursorReadHdr(pCursor, &pThis->Asn1Core, pszErrorTag);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTAsn1CursorMatchTagClassFlags(pCursor, &pThis->Asn1Core, ASN1_TAG_BOOLEAN,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE, fFlags, pszErrorTag, "BOOLEAN");
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->Asn1Core.cb == 1)
+ {
+ RTAsn1CursorSkip(pCursor, pThis->Asn1Core.cb);
+ pThis->Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
+ pThis->Asn1Core.pOps = &g_RTAsn1Boolean_Vtable;
+
+ pThis->fValue = *pThis->Asn1Core.uData.pu8 != 0;
+ if ( *pThis->Asn1Core.uData.pu8 == 0
+ || *pThis->Asn1Core.uData.pu8 == 0xff
+ || !(pCursor->fFlags & (RTASN1CURSOR_FLAGS_DER | RTASN1CURSOR_FLAGS_CER)) )
+ return VINF_SUCCESS;
+
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_BOOLEAN_ENCODING,
+ "%s: Invalid CER/DER boolean value: %#x, valid: 0, 0xff",
+ pszErrorTag, *pThis->Asn1Core.uData.pu8);
+ }
+ else
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_BOOLEAN_ENCODING, "%s: Invalid boolean length, exepcted 1: %#x",
+ pszErrorTag, pThis->Asn1Core.cb);
+ }
+ }
+ RT_ZERO(*pThis);
+ return rc;
+}
+
+
+/*
+ * Generate code for the associated collection types.
+ */
+#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-boolean-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+#include <iprt/asn1-generator-asn1-decoder.h>
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-boolean-template.h b/src/VBox/Runtime/common/asn1/asn1-ut-boolean-template.h
new file mode 100644
index 00000000..39ef92e5
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-boolean-template.h
@@ -0,0 +1,55 @@
+/* $Id: asn1-ut-boolean-template.h $ */
+/** @file
+ * IPRT - ASN.1, BOOLEAN Type, Collection Type Template.
+ */
+
+/*
+ * 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
+ */
+
+#define RTASN1TMPL_DECL RTDECL
+
+#define RTASN1TMPL_TYPE RTASN1SEQOFBOOLEANS
+#define RTASN1TMPL_EXT_NAME RTAsn1SeqOfBooleans
+#define RTASN1TMPL_INT_NAME rtAsn1SeqOfBooleans
+RTASN1TMPL_SEQ_OF(RTASN1BOOLEAN, RTAsn1Boolean);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+#define RTASN1TMPL_TYPE RTASN1SETOFBOOLEANS
+#define RTASN1TMPL_EXT_NAME RTAsn1SetOfBooleans
+#define RTASN1TMPL_INT_NAME rtAsn1SetOfBooleans
+RTASN1TMPL_SET_OF(RTASN1BOOLEAN, RTAsn1Boolean);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-boolean.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-boolean.cpp
new file mode 100644
index 00000000..7b0485e5
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-boolean.cpp
@@ -0,0 +1,218 @@
+/* $Id: asn1-ut-boolean.cpp $ */
+/** @file
+ * IPRT - ASN.1, BOOLEAN Type.
+ */
+
+/*
+ * 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/bignum.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+
+#include <iprt/formats/asn1.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The false value (DER & CER). */
+static const uint8_t g_bFalse = 0;
+/** The true value (DER & CER). */
+static const uint8_t g_bTrue = 0xff;
+
+
+/*
+ * ASN.1 BOOLEAN - Special Methods.
+ */
+
+RTDECL(int) RTAsn1Boolean_InitDefault(PRTASN1BOOLEAN pThis, bool fValue, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ RT_NOREF_PV(pAllocator);
+ RTAsn1Core_InitEx(&pThis->Asn1Core, ASN1_TAG_BOOLEAN, ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ &g_RTAsn1Boolean_Vtable, RTASN1CORE_F_DEFAULT | RTASN1CORE_F_PRIMITE_TAG_STRUCT);
+ pThis->fValue = fValue;
+ pThis->Asn1Core.uData.pv = (void *)(fValue ? &g_bTrue : &g_bFalse);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1Boolean_Set(PRTASN1BOOLEAN pThis, bool fValue)
+{
+ /* Since we don't need an allocator, let's automatically initialize the struct. */
+ if (!RTAsn1Boolean_IsPresent(pThis))
+ RTAsn1Boolean_Init(pThis, NULL);
+ else
+ RTAsn1ContentFree(&pThis->Asn1Core);
+ pThis->fValue = fValue;
+ pThis->Asn1Core.uData.pv = (void *)(fValue ? &g_bTrue : &g_bFalse);
+ pThis->Asn1Core.cb = 1;
+ pThis->Asn1Core.fFlags &= ~RTASN1CORE_F_DEFAULT;
+ pThis->Asn1Core.fFlags |= RTASN1CORE_F_PRESENT;
+ return VINF_SUCCESS;
+}
+
+
+
+/*
+ * ASN.1 BOOLEAN - Standard Methods.
+ */
+
+RT_DECL_DATA_CONST(RTASN1COREVTABLE const) g_RTAsn1Boolean_Vtable =
+{
+ "RTAsn1Boolean",
+ sizeof(RTASN1BOOLEAN),
+ ASN1_TAG_BOOLEAN,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ 0,
+ (PFNRTASN1COREVTDTOR)RTAsn1Boolean_Delete,
+ NULL,
+ (PFNRTASN1COREVTCLONE)RTAsn1Boolean_Clone,
+ (PFNRTASN1COREVTCOMPARE)RTAsn1Boolean_Compare,
+ (PFNRTASN1COREVTCHECKSANITY)RTAsn1Boolean_CheckSanity,
+ NULL,
+ NULL
+};
+
+
+RTDECL(int) RTAsn1Boolean_Init(PRTASN1BOOLEAN pThis, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ RT_NOREF_PV(pAllocator);
+ RTAsn1Core_InitEx(&pThis->Asn1Core, ASN1_TAG_BOOLEAN, ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ &g_RTAsn1Boolean_Vtable, RTASN1CORE_F_PRESENT | RTASN1CORE_F_PRIMITE_TAG_STRUCT);
+ pThis->fValue = true;
+ pThis->Asn1Core.cb = 1;
+ pThis->Asn1Core.uData.pv = (void *)&g_bTrue;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1Boolean_Clone(PRTASN1BOOLEAN pThis, PCRTASN1BOOLEAN pSrc, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ AssertPtr(pSrc); AssertPtr(pThis); AssertPtr(pAllocator);
+ RT_ZERO(*pThis);
+ if (RTAsn1Boolean_IsPresent(pSrc))
+ {
+ AssertReturn(pSrc->Asn1Core.pOps == &g_RTAsn1Boolean_Vtable, VERR_INTERNAL_ERROR_3);
+ AssertReturn(pSrc->Asn1Core.cb <= 1, VERR_INTERNAL_ERROR_4);
+
+ int rc;
+ if ( pSrc->Asn1Core.cb == 1
+ && pSrc->Asn1Core.uData.pu8[0] != 0x00
+ && pSrc->Asn1Core.uData.pu8[0] != 0xff)
+ {
+ /* DER/CER incompatible value must be copied as-is. */
+ rc = RTAsn1Core_CloneContent(&pThis->Asn1Core, &pSrc->Asn1Core, pAllocator);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ else
+ {
+ /* No value or one of the standard values. */
+ rc = RTAsn1Core_CloneNoContent(&pThis->Asn1Core, &pSrc->Asn1Core);
+ if (RT_FAILURE(rc))
+ return rc;
+ pThis->Asn1Core.uData.pv = (void *)(pSrc->fValue ? &g_bTrue : &g_bFalse);
+ }
+ pThis->fValue = pSrc->fValue;
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(void) RTAsn1Boolean_Delete(PRTASN1BOOLEAN pThis)
+{
+ if ( pThis
+ && RTAsn1Boolean_IsPresent(pThis))
+ {
+ Assert(pThis->Asn1Core.pOps == &g_RTAsn1Boolean_Vtable);
+ Assert(pThis->Asn1Core.cb <= 1);
+
+ RTAsn1ContentFree(&pThis->Asn1Core);
+ RT_ZERO(*pThis);
+ }
+}
+
+
+RTDECL(int) RTAsn1Boolean_Enum(PRTASN1BOOLEAN pThis, PFNRTASN1ENUMCALLBACK pfnCallback, uint32_t uDepth, void *pvUser)
+{
+ Assert(pThis && (!RTAsn1Boolean_IsPresent(pThis) || pThis->Asn1Core.pOps == &g_RTAsn1Boolean_Vtable));
+ RT_NOREF_PV(pThis); RT_NOREF_PV(pfnCallback); RT_NOREF_PV(uDepth); RT_NOREF_PV(pvUser);
+
+ /* No children to enumerate. */
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1Boolean_Compare(PCRTASN1BOOLEAN pLeft, PCRTASN1BOOLEAN pRight)
+{
+ Assert(pLeft && (!RTAsn1Boolean_IsPresent(pLeft) || pLeft->Asn1Core.pOps == &g_RTAsn1Boolean_Vtable));
+ Assert(pRight && (!RTAsn1Boolean_IsPresent(pRight) || pRight->Asn1Core.pOps == &g_RTAsn1Boolean_Vtable));
+
+ int iDiff;
+ if (RTAsn1Boolean_IsPresent(pLeft))
+ {
+ if (RTAsn1Boolean_IsPresent(pRight))
+ iDiff = (int)pLeft->fValue - (int)pRight->fValue;
+ else
+ iDiff = -1;
+ }
+ else
+ iDiff = 0 - (int)RTAsn1Boolean_IsPresent(pRight);
+ return iDiff;
+}
+
+
+RTDECL(int) RTAsn1Boolean_CheckSanity(PCRTASN1BOOLEAN pThis, uint32_t fFlags, PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ if (RT_UNLIKELY(!RTAsn1Boolean_IsPresent(pThis)))
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_NOT_PRESENT, "%s: Missing (BOOLEAN).", pszErrorTag);
+ RT_NOREF_PV(fFlags);
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * Generate code for the associated collection types.
+ */
+#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-boolean-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+#include <iprt/asn1-generator-core.h>
+#include <iprt/asn1-generator-init.h>
+#include <iprt/asn1-generator-sanity.h>
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-core-decode.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-core-decode.cpp
new file mode 100644
index 00000000..3015ab89
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-core-decode.cpp
@@ -0,0 +1,69 @@
+/* $Id: asn1-ut-core-decode.cpp $ */
+/** @file
+ * IPRT - ASN.1, Generic Core Type, Decoding.
+ */
+
+/*
+ * 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/errcore.h>
+#include <iprt/string.h>
+
+
+RTDECL(int) RTAsn1Core_DecodeAsn1(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1CORE pThis, const char *pszErrorTag)
+{
+ int rc = RTAsn1CursorReadHdr(pCursor, pThis, pszErrorTag);
+ if (RT_SUCCESS(rc))
+ {
+ RTAsn1CursorSkip(pCursor, pThis->cb);
+ pThis->pOps = &g_RTAsn1Core_Vtable;
+ return VINF_SUCCESS;
+ }
+ RT_NOREF_PV(fFlags);
+ RT_ZERO(*pThis);
+ return rc;
+}
+
+
+/*
+ * Generate code for the associated collection types.
+ */
+#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-core-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+#include <iprt/asn1-generator-asn1-decoder.h>
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-core-template.h b/src/VBox/Runtime/common/asn1/asn1-ut-core-template.h
new file mode 100644
index 00000000..10a9b995
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-core-template.h
@@ -0,0 +1,55 @@
+/* $Id: asn1-ut-core-template.h $ */
+/** @file
+ * IPRT - ASN.1, Generic Core Type, Collection Type Template.
+ */
+
+/*
+ * 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
+ */
+
+#define RTASN1TMPL_DECL RTDECL
+
+#define RTASN1TMPL_TYPE RTASN1SEQOFCORES
+#define RTASN1TMPL_EXT_NAME RTAsn1SeqOfCores
+#define RTASN1TMPL_INT_NAME rtAsn1SeqOfCores
+RTASN1TMPL_SEQ_OF(RTASN1CORE, RTAsn1Core);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+#define RTASN1TMPL_TYPE RTASN1SETOFCORES
+#define RTASN1TMPL_EXT_NAME RTAsn1SetOfCores
+#define RTASN1TMPL_INT_NAME rtAsn1SetOfCores
+RTASN1TMPL_SET_OF(RTASN1CORE, RTAsn1Core);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-core.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-core.cpp
new file mode 100644
index 00000000..b9ff385f
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-core.cpp
@@ -0,0 +1,331 @@
+/* $Id: asn1-ut-core.cpp $ */
+/** @file
+ * IPRT - ASN.1, Generic Core Type.
+ */
+
+/*
+ * 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>
+
+
+/*
+ * ASN.1 Core - Special methods (for all applications of RTASN1CORE).
+ */
+
+RTDECL(int) RTAsn1Core_SetTagAndFlags(PRTASN1CORE pAsn1Core, uint32_t uTag, uint8_t fClass)
+{
+ if (!(pAsn1Core->fFlags & RTASN1CORE_F_TAG_IMPLICIT))
+ {
+ pAsn1Core->fRealClass = pAsn1Core->fClass;
+ pAsn1Core->uRealTag = pAsn1Core->uTag;
+ Assert(pAsn1Core->uRealTag == pAsn1Core->uTag);
+ pAsn1Core->fFlags |= RTASN1CORE_F_TAG_IMPLICIT;
+ }
+ pAsn1Core->uTag = uTag;
+ pAsn1Core->fClass = fClass;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1Core_ChangeTag(PRTASN1CORE pAsn1Core, uint32_t uTag)
+{
+ if (!(pAsn1Core->fFlags & RTASN1CORE_F_TAG_IMPLICIT))
+ pAsn1Core->uTag = uTag;
+ pAsn1Core->uRealTag = uTag;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(void) RTAsn1Core_ResetImplict(PRTASN1CORE pThis)
+{
+ AssertPtr(pThis);
+ if (pThis->fFlags & RTASN1CORE_F_TAG_IMPLICIT)
+ {
+ pThis->fFlags &= ~RTASN1CORE_F_TAG_IMPLICIT;
+ pThis->uTag = pThis->uRealTag;
+ pThis->fClass = pThis->fRealClass;
+ }
+}
+
+
+RTDECL(int) RTAsn1Core_InitEx(PRTASN1CORE pAsn1Core, uint32_t uTag, uint8_t fClass, PCRTASN1COREVTABLE pOps, uint32_t fFlags)
+{
+ pAsn1Core->uTag = uTag;
+ pAsn1Core->fClass = fClass;
+ pAsn1Core->uRealTag = uTag;
+ pAsn1Core->fRealClass = fClass;
+ pAsn1Core->cbHdr = 0;
+ pAsn1Core->cb = 0;
+ pAsn1Core->fFlags = fFlags;
+ pAsn1Core->uData.pv = NULL;
+ pAsn1Core->pOps = pOps;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1Core_InitDefault(PRTASN1CORE pAsn1Core, uint32_t uTag, uint8_t fClass)
+{
+ return RTAsn1Core_InitEx(pAsn1Core, uTag, fClass, NULL, RTASN1CORE_F_DEFAULT);
+}
+
+
+static int rtAsn1Core_CloneEx(PRTASN1CORE pThis, PCRTASN1CORE pSrc, PCRTASN1ALLOCATORVTABLE pAllocator, bool fCopyContent)
+{
+ Assert(RTASN1CORE_IS_PRESENT(pSrc));
+ pThis->uTag = pSrc->uTag;
+ pThis->fClass = pSrc->fClass;
+ pThis->uRealTag = pSrc->uRealTag;
+ pThis->fRealClass = pSrc->fRealClass;
+ pThis->cbHdr = pSrc->cbHdr;
+ pThis->fFlags = pSrc->fFlags & ~(RTASN1CORE_F_ALLOCATED_CONTENT | RTASN1CORE_F_DECODED_CONTENT);
+ pThis->pOps = pSrc->pOps;
+ pThis->cb = 0;
+ pThis->uData.pv = NULL;
+ if (pSrc->cb)
+ {
+ if (!fCopyContent)
+ pThis->cb = pSrc->cb;
+ else
+ {
+ int rc = RTAsn1ContentDup(pThis, pSrc->uData.pv, pSrc->cb, pAllocator);
+ if (RT_FAILURE(rc))
+ {
+ RT_ZERO(*pThis);
+ return rc;
+ }
+ Assert(pThis->cb == pSrc->cb);
+ AssertPtr(pThis->uData.pv);
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1Core_CloneContent(PRTASN1CORE pThis, PCRTASN1CORE pSrc, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ return rtAsn1Core_CloneEx(pThis, pSrc, pAllocator, true /*fCopyContent*/);
+}
+
+
+RTDECL(int) RTAsn1Core_CloneNoContent(PRTASN1CORE pThis, PCRTASN1CORE pSrc)
+{
+ return rtAsn1Core_CloneEx(pThis, pSrc, NULL, false /*fCopyContent*/);
+}
+
+
+RTDECL(int) RTAsn1Core_CompareEx(PCRTASN1CORE pLeft, PCRTASN1CORE pRight, bool fIgnoreTagAndClass)
+{
+ int iDiff;
+ if (RTASN1CORE_IS_PRESENT(pLeft))
+ {
+ if (RTASN1CORE_IS_PRESENT(pRight))
+ {
+ iDiff = memcmp(pLeft->uData.pv, pRight->uData.pv, RT_MIN(pLeft->cb, pRight->cb));
+ if (!iDiff)
+ {
+ if (pLeft->cb != pRight->cb)
+ iDiff = pLeft->cb < pRight->cb ? -1 : 1;
+ else if (!fIgnoreTagAndClass)
+ {
+ if (pLeft->uTag != pRight->uTag)
+ iDiff = pLeft->uTag < pRight->uTag ? -1 : 1;
+ else if (pLeft->fClass != pRight->fClass)
+ iDiff = pLeft->fClass < pRight->fClass ? -1 : 1;
+ }
+ }
+ else
+ iDiff = iDiff < 0 ? -1 : 1;
+ }
+ else
+ iDiff = -1;
+ }
+ else
+ iDiff = 0 - (int)RTASN1CORE_IS_PRESENT(pRight);
+ return iDiff;
+}
+
+
+/**
+ * @interface_method_impl{RTASN1COREVTABLE,pfnEncodePrep,
+ * This is for not dropping the unparsed content of a 'core' structure when
+ * re-encoding it. }
+ */
+static DECLCALLBACK(int) rtAsn1Core_EncodePrep(PRTASN1CORE pThisCore, uint32_t fFlags, PRTERRINFO pErrInfo)
+{
+ /* We don't update anything here. */
+ RT_NOREF(pThisCore, fFlags, pErrInfo);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTASN1COREVTABLE,pfnEncodeWrite,
+ * This is for not dropping the unparsed content of a 'core' structure when
+ * re-encoding it. }
+ */
+static DECLCALLBACK(int) rtAsn1Core_EncodeWrite(PRTASN1CORE pThisCore, uint32_t fFlags, PFNRTASN1ENCODEWRITER pfnWriter,
+ void *pvUser, PRTERRINFO pErrInfo)
+{
+ int rc = RTAsn1EncodeWriteHeader(pThisCore, fFlags, pfnWriter, pvUser, pErrInfo);
+ if ( RT_SUCCESS(rc)
+ && rc != VINF_ASN1_NOT_ENCODED)
+ {
+ Assert(!RTASN1CORE_IS_DUMMY(pThisCore));
+ if (pThisCore->cb)
+ {
+ AssertPtrReturn(pThisCore->uData.pv,
+ RTErrInfoSetF(pErrInfo, VERR_ASN1_INVALID_DATA_POINTER,
+ "Invalid uData pointer %p for lone ASN.1 core with %#x bytes of content",
+ pThisCore->uData.pv, pThisCore->cb));
+ rc = pfnWriter(pThisCore->uData.pv, pThisCore->cb, pvUser, pErrInfo);
+ }
+ }
+ return rc;
+}
+
+
+
+/*
+ * ASN.1 Core - Standard Methods.
+ *
+ * @note Children of the ASN.1 Core doesn't normally call these, they are for
+ * when RTASN1CORE is used as a member type.
+ */
+
+RT_DECL_DATA_CONST(RTASN1COREVTABLE const) g_RTAsn1Core_Vtable =
+{
+ "RTAsn1Core",
+ sizeof(RTASN1CORE),
+ UINT8_MAX,
+ UINT8_MAX,
+ 0,
+ RTAsn1Core_Delete,
+ RTAsn1Core_Enum,
+ (PFNRTASN1COREVTCLONE)RTAsn1Core_Clone,
+ (PFNRTASN1COREVTCOMPARE)RTAsn1Core_Compare,
+ (PFNRTASN1COREVTCHECKSANITY)RTAsn1Core_CheckSanity,
+ rtAsn1Core_EncodePrep,
+ rtAsn1Core_EncodeWrite
+};
+
+
+RTDECL(int) RTAsn1Core_Init(PRTASN1CORE pThis, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ RT_NOREF_PV(pAllocator);
+ return RTAsn1Core_InitEx(pThis, 0, ASN1_TAGCLASS_CONTEXT | ASN1_TAGFLAG_PRIMITIVE,
+ &g_RTAsn1Core_Vtable, RTASN1CORE_F_PRESENT);
+}
+
+
+RTDECL(int) RTAsn1Core_Clone(PRTASN1CORE pThis, PCRTASN1CORE pSrc, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ int rc;
+ RT_ZERO(*pThis);
+ if (RTASN1CORE_IS_PRESENT(pSrc))
+ {
+ Assert(pSrc->pOps == &g_RTAsn1Core_Vtable);
+
+ rc = RTAsn1Core_CloneContent(pThis, pSrc, pAllocator);
+ }
+ else
+ rc = VINF_SUCCESS;
+ return rc;
+}
+
+
+RTDECL(void) RTAsn1Core_Delete(PRTASN1CORE pThis)
+{
+ if (pThis && RTASN1CORE_IS_PRESENT(pThis))
+ {
+ Assert(pThis->pOps == &g_RTAsn1Core_Vtable);
+
+ RTAsn1ContentFree(pThis);
+ RT_ZERO(*pThis);
+ }
+}
+
+
+RTDECL(int) RTAsn1Core_Enum(PRTASN1CORE pThis, PFNRTASN1ENUMCALLBACK pfnCallback, uint32_t uDepth, void *pvUser)
+{
+ /* We have no children to enumerate. */
+ Assert(pThis && (!RTASN1CORE_IS_PRESENT(pThis) || pThis->pOps == &g_RTAsn1Core_Vtable));
+ NOREF(pThis);
+ NOREF(pfnCallback);
+ NOREF(uDepth);
+ NOREF(pvUser);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1Core_Compare(PCRTASN1CORE pLeft, PCRTASN1CORE pRight)
+{
+ Assert(pLeft && (!RTASN1CORE_IS_PRESENT(pLeft) || pLeft->pOps == &g_RTAsn1Core_Vtable));
+ Assert(pRight && (!RTASN1CORE_IS_PRESENT(pRight) || pRight->pOps == &g_RTAsn1Core_Vtable));
+
+ return RTAsn1Core_CompareEx(pLeft, pRight, false /*fIgnoreTagAndClass*/);
+}
+
+
+RTDECL(int) RTAsn1Core_CheckSanity(PCRTASN1CORE pThis, uint32_t fFlags, PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ RT_NOREF_PV(fFlags);
+
+ /* We can only check that it's present. */
+ if (!RTAsn1Core_IsPresent(pThis))
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_NOT_PRESENT, "%s: Missing (RTASN1CORE).", pszErrorTag);
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * Generate code for the associated collection types.
+ */
+#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-core-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+#include <iprt/asn1-generator-core.h>
+#include <iprt/asn1-generator-init.h>
+#include <iprt/asn1-generator-sanity.h>
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-dyntype-decode.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-dyntype-decode.cpp
new file mode 100644
index 00000000..bc48688a
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-dyntype-decode.cpp
@@ -0,0 +1,250 @@
+/* $Id: asn1-ut-dyntype-decode.cpp $ */
+/** @file
+ * IPRT - ASN.1, Dynamic Type, Decoding.
+ */
+
+/*
+ * 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/err.h>
+#include <iprt/string.h>
+
+#include <iprt/formats/asn1.h>
+
+
+
+RTDECL(int) RTAsn1DynType_DecodeAsn1(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1DYNTYPE pDynType, const char *pszErrorTag)
+{
+ RT_ZERO(*pDynType);
+
+ Assert(!(fFlags & RTASN1CURSOR_GET_F_IMPLICIT)); RT_NOREF_PV(fFlags);
+ uint32_t cbSavedLeft = pCursor->cbLeft;
+ uint8_t const *pbSavedCur = pCursor->pbCur;
+
+ int rc = RTAsn1CursorReadHdr(pCursor, &pDynType->u.Core, pszErrorTag);
+ if (RT_SUCCESS(rc))
+ {
+ pDynType->enmType = RTASN1TYPE_CORE;
+
+ if (pDynType->u.Core.fClass == (ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE))
+ {
+ switch (pDynType->u.Core.uTag)
+ {
+ case ASN1_TAG_BOOLEAN:
+ pDynType->enmType = RTASN1TYPE_BOOLEAN;
+ break;
+ case ASN1_TAG_INTEGER:
+ pDynType->enmType = RTASN1TYPE_INTEGER;
+ break;
+ //case ASN1_TAG_ENUMERATED:
+ // pDynType->enmType = RTASN1TYPE_ENUMERATED;
+ // break;
+ //case ASN1_TAG_REAL:
+ // pDynType->enmType = RTASN1TYPE_REAL;
+ // break;
+ case ASN1_TAG_BIT_STRING:
+ pDynType->enmType = RTASN1TYPE_BIT_STRING;
+ break;
+ case ASN1_TAG_OCTET_STRING:
+ pDynType->enmType = RTASN1TYPE_OCTET_STRING;
+ break;
+ case ASN1_TAG_NULL:
+ pDynType->enmType = RTASN1TYPE_NULL;
+ break;
+ case ASN1_TAG_SEQUENCE:
+ RT_ZERO(*pDynType);
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_DYNTYPE_BAD_TAG, "ASN.1 SEQUENCE shall be constructed.");
+ case ASN1_TAG_SET:
+ RT_ZERO(*pDynType);
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_DYNTYPE_BAD_TAG, "ASN.1 SET shall be constructed.");
+ case ASN1_TAG_OID:
+ pDynType->enmType = RTASN1TYPE_OBJID;
+ break;
+ //case ASN1_TAG_RELATIVE_OID:
+ // pDynType->enmType = RTASN1TYPE_RELATIVE_OBJID;
+ // break;
+ case ASN1_TAG_UTC_TIME:
+ case ASN1_TAG_GENERALIZED_TIME:
+ pDynType->enmType = RTASN1TYPE_TIME;
+ break;
+ case ASN1_TAG_UTF8_STRING:
+ case ASN1_TAG_NUMERIC_STRING:
+ case ASN1_TAG_PRINTABLE_STRING:
+ case ASN1_TAG_T61_STRING:
+ case ASN1_TAG_VIDEOTEX_STRING:
+ case ASN1_TAG_IA5_STRING:
+ case ASN1_TAG_GRAPHIC_STRING:
+ case ASN1_TAG_VISIBLE_STRING:
+ case ASN1_TAG_UNIVERSAL_STRING:
+ case ASN1_TAG_GENERAL_STRING:
+ case ASN1_TAG_BMP_STRING:
+ pDynType->enmType = RTASN1TYPE_STRING;
+ break;
+ //case ASN1_TAG_CHARACTER_STRING:
+ // pDynType->enmType = RTASN1TYPE_CHARACTER_STRING;
+ // break;
+
+ default:
+ RT_ZERO(*pDynType);
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_DYNTYPE_TAG_NOT_IMPL,
+ "Primitive tag %u (%#x) not implemented.",
+ pDynType->u.Core.uTag, pDynType->u.Core.uTag);
+ }
+ }
+ else if (pDynType->u.Core.fClass == (ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_CONSTRUCTED))
+ switch (pDynType->u.Core.uTag)
+ {
+ case ASN1_TAG_BOOLEAN:
+ RT_ZERO(*pDynType);
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_DYNTYPE_BAD_TAG, "ASN.1 BOOLEAN shall be primitive.");
+ case ASN1_TAG_INTEGER:
+ RT_ZERO(*pDynType);
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_DYNTYPE_BAD_TAG, "ASN.1 BOOLEAN shall be primitive.");
+ case ASN1_TAG_ENUMERATED:
+ RT_ZERO(*pDynType);
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_DYNTYPE_BAD_TAG, "ASN.1 ENUMERATED shall be primitive.");
+ case ASN1_TAG_REAL:
+ RT_ZERO(*pDynType);
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_DYNTYPE_BAD_TAG, "ASN.1 REAL shall be primitive.");
+ case ASN1_TAG_BIT_STRING:
+ pDynType->enmType = RTASN1TYPE_BIT_STRING;
+ break;
+ case ASN1_TAG_OCTET_STRING:
+ pDynType->enmType = RTASN1TYPE_OCTET_STRING;
+ break;
+ case ASN1_TAG_NULL:
+ RT_ZERO(*pDynType);
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_DYNTYPE_BAD_TAG, "ASN.1 NULL shall be primitive.");
+ case ASN1_TAG_SEQUENCE:
+#if 0
+ pDynType->enmType = RTASN1TYPE_SEQUENCE_CORE;
+ pDynType->u.SeqCore.Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
+ RTAsn1CursorSkip(pCursor, pDynType->u.Core.cb);
+ return VINF_SUCCESS;
+#else
+ pDynType->enmType = RTASN1TYPE_CORE;
+#endif
+ break;
+ case ASN1_TAG_SET:
+#if 0
+ pDynType->enmType = RTASN1TYPE_SET_CORE;
+ pDynType->u.SeqCore.Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
+ RTAsn1CursorSkip(pCursor, pDynType->u.Core.cb);
+ return VINF_SUCCESS;
+#else
+ pDynType->enmType = RTASN1TYPE_CORE;
+#endif
+ break;
+ case ASN1_TAG_OID:
+ RT_ZERO(*pDynType);
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_DYNTYPE_BAD_TAG, "ASN.1 OBJECT ID shall be primitive.");
+ case ASN1_TAG_RELATIVE_OID:
+ RT_ZERO(*pDynType);
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_DYNTYPE_BAD_TAG, "ASN.1 RELATIVE OID shall be primitive.");
+
+ case ASN1_TAG_UTF8_STRING:
+ case ASN1_TAG_NUMERIC_STRING:
+ case ASN1_TAG_PRINTABLE_STRING:
+ case ASN1_TAG_T61_STRING:
+ case ASN1_TAG_VIDEOTEX_STRING:
+ case ASN1_TAG_IA5_STRING:
+ case ASN1_TAG_GRAPHIC_STRING:
+ case ASN1_TAG_VISIBLE_STRING:
+ case ASN1_TAG_UNIVERSAL_STRING:
+ case ASN1_TAG_GENERAL_STRING:
+ case ASN1_TAG_BMP_STRING:
+ pDynType->enmType = RTASN1TYPE_STRING;
+ break;
+ //case ASN1_TAG_CHARACTER_STRING:
+ // pDynType->enmType = RTASN1TYPE_CHARACTER_STRING;
+ // break;
+
+ default:
+ RT_ZERO(*pDynType);
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_DYNTYPE_TAG_NOT_IMPL,
+ "Constructed tag %u (%#x) not implemented.",
+ pDynType->u.Core.uTag, pDynType->u.Core.uTag);
+ }
+ else
+ Assert(pDynType->enmType == RTASN1TYPE_CORE);
+
+ /*
+ * Restore the cursor and redo with specific type.
+ */
+ pCursor->pbCur = pbSavedCur;
+ pCursor->cbLeft = cbSavedLeft;
+
+ switch (pDynType->enmType)
+ {
+ case RTASN1TYPE_INTEGER:
+ rc = RTAsn1Integer_DecodeAsn1(pCursor, 0, &pDynType->u.Integer, pszErrorTag);
+ break;
+ case RTASN1TYPE_BOOLEAN:
+ rc = RTAsn1Boolean_DecodeAsn1(pCursor, 0, &pDynType->u.Boolean, pszErrorTag);
+ break;
+ case RTASN1TYPE_OBJID:
+ rc = RTAsn1ObjId_DecodeAsn1(pCursor, 0, &pDynType->u.ObjId, pszErrorTag);
+ break;
+ case RTASN1TYPE_BIT_STRING:
+ rc = RTAsn1BitString_DecodeAsn1(pCursor, 0, &pDynType->u.BitString, pszErrorTag);
+ break;
+ case RTASN1TYPE_OCTET_STRING:
+ rc = RTAsn1OctetString_DecodeAsn1(pCursor, 0, &pDynType->u.OctetString, pszErrorTag);
+ break;
+ case RTASN1TYPE_NULL:
+ rc = RTAsn1Null_DecodeAsn1(pCursor, 0, &pDynType->u.Asn1Null, pszErrorTag);
+ break;
+ case RTASN1TYPE_TIME:
+ rc = RTAsn1Time_DecodeAsn1(pCursor, 0, &pDynType->u.Time, pszErrorTag);
+ break;
+ case RTASN1TYPE_STRING:
+ rc = RTAsn1String_DecodeAsn1(pCursor, 0, &pDynType->u.String, pszErrorTag);
+ break;
+ case RTASN1TYPE_CORE:
+ rc = RTAsn1Core_DecodeAsn1(pCursor, 0, &pDynType->u.Core, pszErrorTag);
+ break;
+ default:
+ AssertFailedReturn(VERR_INTERNAL_ERROR_4);
+ }
+ if (RT_SUCCESS(rc))
+ return rc;
+ }
+ RT_ZERO(*pDynType);
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-dyntype.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-dyntype.cpp
new file mode 100644
index 00000000..1708c71a
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-dyntype.cpp
@@ -0,0 +1,197 @@
+/* $Id: asn1-ut-dyntype.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/err.h>
+#include <iprt/string.h>
+
+#include <iprt/formats/asn1.h>
+
+
+/*
+ * ASN.1 dynamic type union - Standard Methods.
+ */
+
+
+RTDECL(int) RTAsn1DynType_Init(PRTASN1DYNTYPE pThis, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ RT_NOREF_PV(pAllocator);
+ RT_ZERO(*pThis);
+ pThis->enmType = RTASN1TYPE_NOT_PRESENT;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1DynType_Clone(PRTASN1DYNTYPE pThis, PCRTASN1DYNTYPE pSrc, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ AssertPtr(pSrc); AssertPtr(pThis); AssertPtr(pAllocator);
+ RT_ZERO(*pThis);
+ if (RTAsn1DynType_IsPresent(pSrc))
+ {
+ int rc;
+ switch (pSrc->enmType)
+ {
+ case RTASN1TYPE_CORE: rc = RTAsn1Core_Clone(&pThis->u.Core, &pSrc->u.Core, pAllocator); break;
+ case RTASN1TYPE_NULL: rc = RTAsn1Null_Clone(&pThis->u.Asn1Null, &pSrc->u.Asn1Null, pAllocator); break;
+ case RTASN1TYPE_INTEGER: rc = RTAsn1Integer_Clone(&pThis->u.Integer, &pSrc->u.Integer, pAllocator); break;
+ case RTASN1TYPE_BOOLEAN: rc = RTAsn1Boolean_Clone(&pThis->u.Boolean, &pSrc->u.Boolean, pAllocator); break;
+ case RTASN1TYPE_STRING: rc = RTAsn1String_Clone(&pThis->u.String, &pSrc->u.String, pAllocator); break;
+ case RTASN1TYPE_OCTET_STRING: rc = RTAsn1OctetString_Clone(&pThis->u.OctetString, &pSrc->u.OctetString, pAllocator); break;
+ case RTASN1TYPE_BIT_STRING: rc = RTAsn1BitString_Clone(&pThis->u.BitString, &pSrc->u.BitString, pAllocator); break;
+ case RTASN1TYPE_TIME: rc = RTAsn1Time_Clone(&pThis->u.Time, &pSrc->u.Time, pAllocator); break;
+#if 0
+ case RTASN1TYPE_SEQUENCE_CORE: rc = VERR_NOT_SUPPORTED; //rc = RTAsn1SequenceCore_Clone(&pThis->u.SeqCore, &pSrc->u.SeqCore, pAllocator); break;
+ case RTASN1TYPE_SET_CORE: rc = VERR_NOT_SUPPORTED; //rc = RTAsn1SetCore_Clone(&pThis->u.SetCore, &pSrc->u.SetCore, pAllocator); break;
+#endif
+ case RTASN1TYPE_OBJID: rc = RTAsn1ObjId_Clone(&pThis->u.ObjId, &pSrc->u.ObjId, pAllocator); break;
+ default:
+ AssertFailedReturn(VERR_ASN1_INTERNAL_ERROR_2);
+ }
+ if (RT_FAILURE(rc))
+ {
+ RT_ZERO(*pThis);
+ return rc;
+ }
+ pThis->enmType = pSrc->enmType;
+ }
+ else
+ pThis->enmType = RTASN1TYPE_NOT_PRESENT;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(void) RTAsn1DynType_Delete(PRTASN1DYNTYPE pThis)
+{
+ if ( pThis
+ && RTAsn1DynType_IsPresent(pThis))
+ {
+ if ( pThis->u.Core.pOps
+ && pThis->u.Core.pOps->pfnDtor)
+ pThis->u.Core.pOps->pfnDtor(&pThis->u.Core);
+ RT_ZERO(*pThis);
+ }
+}
+
+
+RTDECL(int) RTAsn1DynType_SetToNull(PRTASN1DYNTYPE pThis)
+{
+ RTAsn1DynType_Delete(pThis);
+ pThis->enmType = RTASN1TYPE_NULL;
+ return RTAsn1Null_Init(&pThis->u.Asn1Null, NULL /*pAllocator*/);
+}
+
+
+RTDECL(int) RTAsn1DynType_SetToObjId(PRTASN1DYNTYPE pThis, PCRTASN1OBJID pSrc, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ RTAsn1DynType_Delete(pThis);
+ pThis->enmType = RTASN1TYPE_OBJID;
+ return RTAsn1ObjId_Clone(&pThis->u.ObjId, pSrc, pAllocator);
+}
+
+
+RTDECL(int) RTAsn1DynType_Enum(PRTASN1DYNTYPE pThis, PFNRTASN1ENUMCALLBACK pfnCallback, uint32_t uDepth, void *pvUser)
+{
+ if ( pThis
+ && RTAsn1DynType_IsPresent(pThis))
+ {
+ if ( pThis->u.Core.pOps
+ && pThis->u.Core.pOps->pfnEnum)
+ return pThis->u.Core.pOps->pfnEnum(&pThis->u.Core, pfnCallback, uDepth, pvUser);
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1DynType_Compare(PCRTASN1DYNTYPE pLeft, PCRTASN1DYNTYPE pRight)
+{
+ if (RTAsn1DynType_IsPresent(pLeft) && RTAsn1DynType_IsPresent(pRight))
+ {
+ if (pLeft->enmType != pRight->enmType)
+ return pLeft->enmType < pRight->enmType ? -1 : 1;
+
+ switch (pLeft->enmType)
+ {
+ case RTASN1TYPE_CORE: return RTAsn1Core_Compare(&pLeft->u.Core, &pRight->u.Core); break;
+ case RTASN1TYPE_NULL: return RTAsn1Null_Compare(&pLeft->u.Asn1Null, &pRight->u.Asn1Null);
+ case RTASN1TYPE_INTEGER: return RTAsn1Integer_Compare(&pLeft->u.Integer, &pRight->u.Integer);
+ case RTASN1TYPE_BOOLEAN: return RTAsn1Boolean_Compare(&pLeft->u.Boolean, &pRight->u.Boolean);
+ case RTASN1TYPE_STRING: return RTAsn1String_Compare(&pLeft->u.String, &pRight->u.String);
+ case RTASN1TYPE_OCTET_STRING: return RTAsn1OctetString_Compare(&pLeft->u.OctetString, &pRight->u.OctetString);
+ case RTASN1TYPE_BIT_STRING: return RTAsn1BitString_Compare(&pLeft->u.BitString, &pRight->u.BitString);
+ case RTASN1TYPE_TIME: return RTAsn1Time_Compare(&pLeft->u.Time, &pRight->u.Time);
+ case RTASN1TYPE_OBJID: return RTAsn1ObjId_Compare(&pLeft->u.ObjId, &pRight->u.ObjId);
+ default: AssertFailedReturn(-1);
+ }
+ }
+ else
+ return (int)RTAsn1DynType_IsPresent(pLeft) - (int)RTAsn1DynType_IsPresent(pRight);
+}
+
+
+RTDECL(int) RTAsn1DynType_CheckSanity(PCRTASN1DYNTYPE pThis, uint32_t fFlags, PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ if (RT_UNLIKELY(!RTAsn1DynType_IsPresent(pThis)))
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_NOT_PRESENT, "%s: Missing (DYNTYPE).", pszErrorTag);
+
+ int rc;
+ switch (pThis->enmType)
+ {
+ case RTASN1TYPE_CORE: rc = RTAsn1Core_CheckSanity(&pThis->u.Core, fFlags, pErrInfo, pszErrorTag); break;
+ case RTASN1TYPE_NULL: rc = RTAsn1Null_CheckSanity(&pThis->u.Asn1Null, fFlags, pErrInfo, pszErrorTag); break;
+ case RTASN1TYPE_INTEGER: rc = RTAsn1Integer_CheckSanity(&pThis->u.Integer, fFlags, pErrInfo, pszErrorTag); break;
+ case RTASN1TYPE_BOOLEAN: rc = RTAsn1Boolean_CheckSanity(&pThis->u.Boolean, fFlags, pErrInfo, pszErrorTag); break;
+ case RTASN1TYPE_STRING: rc = RTAsn1String_CheckSanity(&pThis->u.String, fFlags, pErrInfo, pszErrorTag); break;
+ case RTASN1TYPE_OCTET_STRING: rc = RTAsn1OctetString_CheckSanity(&pThis->u.OctetString, fFlags, pErrInfo, pszErrorTag); break;
+ case RTASN1TYPE_BIT_STRING: rc = RTAsn1BitString_CheckSanity(&pThis->u.BitString, fFlags, pErrInfo, pszErrorTag); break;
+ case RTASN1TYPE_TIME: rc = RTAsn1Time_CheckSanity(&pThis->u.Time, fFlags, pErrInfo, pszErrorTag); break;
+#if 0
+ case RTASN1TYPE_SEQUENCE_CORE: rc = VINF_SUCCESS; //rc = RTAsn1SequenceCore_CheckSanity(&pThis->u.SeqCore, fFlags, pErrInfo, pszErrorTag); break;
+ case RTASN1TYPE_SET_CORE: rc = VINF_SUCCESS; //rc = RTAsn1SetCore_CheckSanity(&pThis->u.SetCore, fFlags, pErrInfo, pszErrorTag); break;
+#endif
+ case RTASN1TYPE_OBJID: rc = RTAsn1ObjId_CheckSanity(&pThis->u.ObjId, fFlags, pErrInfo, pszErrorTag); break;
+ default:
+ AssertFailedReturn(VERR_ASN1_INTERNAL_ERROR_2);
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-integer-decode.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-integer-decode.cpp
new file mode 100644
index 00000000..f753d453
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-integer-decode.cpp
@@ -0,0 +1,97 @@
+/* $Id: asn1-ut-integer-decode.cpp $ */
+/** @file
+ * IPRT - ASN.1, INTEGER Type, Decoding.
+ */
+
+/*
+ * 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/err.h>
+#include <iprt/string.h>
+
+#include <iprt/formats/asn1.h>
+
+
+RTDECL(int) RTAsn1Integer_DecodeAsn1(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1INTEGER pThis, const char *pszErrorTag)
+{
+ pThis->uValue.u = 0;
+ int rc = RTAsn1CursorReadHdr(pCursor, &pThis->Asn1Core, pszErrorTag);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTAsn1CursorMatchTagClassFlags(pCursor, &pThis->Asn1Core, ASN1_TAG_INTEGER,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ fFlags, pszErrorTag, "INTEGER");
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->Asn1Core.cb > 0)
+ {
+ uint32_t offLast = pThis->Asn1Core.cb - 1;
+ switch (pThis->Asn1Core.cb)
+ {
+ default:
+ case 8: pThis->uValue.u |= (uint64_t)pThis->Asn1Core.uData.pu8[offLast - 7] << 56; RT_FALL_THRU();
+ case 7: pThis->uValue.u |= (uint64_t)pThis->Asn1Core.uData.pu8[offLast - 6] << 48; RT_FALL_THRU();
+ case 6: pThis->uValue.u |= (uint64_t)pThis->Asn1Core.uData.pu8[offLast - 5] << 40; RT_FALL_THRU();
+ case 5: pThis->uValue.u |= (uint64_t)pThis->Asn1Core.uData.pu8[offLast - 4] << 32; RT_FALL_THRU();
+ case 4: pThis->uValue.u |= (uint32_t)pThis->Asn1Core.uData.pu8[offLast - 3] << 24; RT_FALL_THRU();
+ case 3: pThis->uValue.u |= (uint32_t)pThis->Asn1Core.uData.pu8[offLast - 2] << 16; RT_FALL_THRU();
+ case 2: pThis->uValue.u |= (uint16_t)pThis->Asn1Core.uData.pu8[offLast - 1] << 8; RT_FALL_THRU();
+ case 1: pThis->uValue.u |= pThis->Asn1Core.uData.pu8[offLast];
+ }
+ RTAsn1CursorSkip(pCursor, pThis->Asn1Core.cb);
+ pThis->Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
+ pThis->Asn1Core.pOps = &g_RTAsn1Integer_Vtable;
+ return VINF_SUCCESS;
+ }
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_INTEGER_ENCODING,
+ "%s: Invalid integer length, exepcted more than 0: %#x",
+ pszErrorTag, pThis->Asn1Core.cb);
+ }
+ }
+ RT_ZERO(*pThis);
+ return rc;
+}
+
+
+/*
+ * Generate code for the associated collection types.
+ */
+#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-integer-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+#include <iprt/asn1-generator-asn1-decoder.h>
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-integer-template.h b/src/VBox/Runtime/common/asn1/asn1-ut-integer-template.h
new file mode 100644
index 00000000..1dfbfa4e
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-integer-template.h
@@ -0,0 +1,55 @@
+/* $Id: asn1-ut-integer-template.h $ */
+/** @file
+ * IPRT - ASN.1, INTEGER Type, Collection Type Template.
+ */
+
+/*
+ * 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
+ */
+
+#define RTASN1TMPL_DECL RTDECL
+
+#define RTASN1TMPL_TYPE RTASN1SEQOFINTEGERS
+#define RTASN1TMPL_EXT_NAME RTAsn1SeqOfIntegers
+#define RTASN1TMPL_INT_NAME rtAsn1SeqOfIntegers
+RTASN1TMPL_SEQ_OF(RTASN1INTEGER, RTAsn1Integer);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+#define RTASN1TMPL_TYPE RTASN1SETOFINTEGERS
+#define RTASN1TMPL_EXT_NAME RTAsn1SetOfIntegers
+#define RTASN1TMPL_INT_NAME rtAsn1SetOfIntegers
+RTASN1TMPL_SET_OF(RTASN1INTEGER, RTAsn1Integer);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-integer.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-integer.cpp
new file mode 100644
index 00000000..79df5c49
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-integer.cpp
@@ -0,0 +1,508 @@
+/* $Id: asn1-ut-integer.cpp $ */
+/** @file
+ * IPRT - ASN.1, INTEGER Type.
+ */
+
+/*
+ * 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/bignum.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+
+#include <iprt/formats/asn1.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Fixed on-byte constants for small numbers.
+ * Good for structure version values and such. */
+static const uint8_t g_abSmall[] =
+{
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+};
+
+
+
+/*
+ * ASN.1 INTEGER - Special Methods.
+ */
+
+
+/**
+ * Updates the native value we keep in RTASN1INTEGER::uValue.
+ *
+ * @param pThis The integer.
+ */
+static void rtAsn1Integer_UpdateNativeValue(PRTASN1INTEGER pThis)
+{
+ uint32_t offLast = pThis->Asn1Core.cb - 1;
+ switch (pThis->Asn1Core.cb)
+ {
+ default: AssertBreak(pThis->Asn1Core.cb > 8); /* paranoia */ RT_FALL_THRU();
+ case 8: pThis->uValue.u |= (uint64_t)pThis->Asn1Core.uData.pu8[offLast - 7] << 56; RT_FALL_THRU();
+ case 7: pThis->uValue.u |= (uint64_t)pThis->Asn1Core.uData.pu8[offLast - 6] << 48; RT_FALL_THRU();
+ case 6: pThis->uValue.u |= (uint64_t)pThis->Asn1Core.uData.pu8[offLast - 5] << 40; RT_FALL_THRU();
+ case 5: pThis->uValue.u |= (uint64_t)pThis->Asn1Core.uData.pu8[offLast - 4] << 32; RT_FALL_THRU();
+ case 4: pThis->uValue.u |= (uint32_t)pThis->Asn1Core.uData.pu8[offLast - 3] << 24; RT_FALL_THRU();
+ case 3: pThis->uValue.u |= (uint32_t)pThis->Asn1Core.uData.pu8[offLast - 2] << 16; RT_FALL_THRU();
+ case 2: pThis->uValue.u |= (uint16_t)pThis->Asn1Core.uData.pu8[offLast - 1] << 8; RT_FALL_THRU();
+ case 1: pThis->uValue.u |= pThis->Asn1Core.uData.pu8[offLast];
+ }
+}
+
+
+RTDECL(int) RTAsn1Integer_InitU64(PRTASN1INTEGER pThis, uint64_t uValue, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ /*
+ * Initialize the core and the native value.
+ */
+ RTAsn1Core_InitEx(&pThis->Asn1Core,
+ ASN1_TAG_INTEGER,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ &g_RTAsn1Integer_Vtable,
+ RTASN1CORE_F_PRESENT | RTASN1CORE_F_PRIMITE_TAG_STRUCT);
+ pThis->uValue.u = uValue;
+
+ /*
+ * Use one of the constants if possible.
+ */
+ if (uValue < RT_ELEMENTS(g_abSmall))
+ {
+ pThis->Asn1Core.cb = 1;
+ pThis->Asn1Core.uData.pv = (void *)&g_abSmall[0];
+ }
+ else
+ {
+ /*
+ * Need to turn uValue into a big endian number without any
+ * unnecessary leading zero bytes.
+ */
+ /* Figure the size. */
+ uint32_t cb = 0;
+ if (uValue <= UINT32_MAX)
+ {
+ if (uValue <= UINT16_MAX)
+ {
+ if (uValue <= UINT8_MAX)
+ cb = 1;
+ else
+ cb = 2;
+ }
+ else
+ {
+ if (uValue <= UINT32_C(0xffffff))
+ cb = 3;
+ else
+ cb = 4;
+ }
+ }
+ else
+ {
+ if (uValue <= UINT64_C(0x0000FfffFfffFfff))
+ {
+ if (uValue <= UINT64_C(0x000000ffFfffFfff))
+ cb = 5;
+ else
+ cb = 6;
+ }
+ else
+ {
+ if (uValue <= UINT64_C(0x00ffFfffFfffFfff))
+ cb = 7;
+ else
+ cb = 8;
+ }
+ }
+
+ /* Allocate space. */
+ int rc = RTAsn1ContentAllocZ(&pThis->Asn1Core, cb, pAllocator);
+ if (RT_FAILURE(rc))
+ {
+ RT_ZERO(*pThis);
+ return rc;
+ }
+
+ /* Serialize the number in MSB order. */
+ uint8_t *pb = (uint8_t *)pThis->Asn1Core.uData.pu8;
+ while (cb-- > 0)
+ {
+ pb[cb] = (uint8_t)uValue;
+ uValue >>= 8;
+ }
+ Assert(uValue == 0);
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1Integer_InitDefault(PRTASN1INTEGER pThis, uint64_t uValue, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ int rc = RTAsn1Integer_InitU64(pThis, uValue, pAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->Asn1Core.fFlags &= ~RTASN1CORE_F_PRESENT;
+ pThis->Asn1Core.fFlags |= RTASN1CORE_F_DEFAULT;
+ }
+ return rc;
+}
+
+
+RTDECL(int32_t) RTAsn1Integer_UnsignedLastBit(PCRTASN1INTEGER pThis)
+{
+ AssertReturn(pThis->Asn1Core.fFlags, -1);
+ uint8_t const *pb = pThis->Asn1Core.uData.pu8;
+ AssertReturn(pb, -1);
+ uint32_t cb = pThis->Asn1Core.cb;
+ AssertReturn(pThis->Asn1Core.cb < (uint32_t)INT32_MAX / 8, -1);
+
+ while (cb-- > 0)
+ {
+ uint8_t b = *pb++;
+ if (b)
+ {
+ int32_t iRet = cb * 8;
+ if (b & 0x80) iRet += 7;
+ else if (b & 0x40) iRet += 6;
+ else if (b & 0x20) iRet += 5;
+ else if (b & 0x10) iRet += 4;
+ else if (b & 0x08) iRet += 3;
+ else if (b & 0x04) iRet += 2;
+ else if (b & 0x02) iRet += 1;
+ else Assert(b == 0x01);
+ return iRet;
+ }
+ }
+ return -1;
+}
+
+
+RTDECL(int) RTAsn1Integer_UnsignedCompare(PCRTASN1INTEGER pLeft, PCRTASN1INTEGER pRight)
+{
+ Assert(pLeft && (!RTAsn1Integer_IsPresent(pLeft) || pLeft->Asn1Core.pOps == &g_RTAsn1Integer_Vtable));
+ Assert(pRight && (!RTAsn1Integer_IsPresent(pRight) || pRight->Asn1Core.pOps == &g_RTAsn1Integer_Vtable));
+
+ int iDiff;
+ if (RTAsn1Integer_IsPresent(pLeft))
+ {
+ if (RTAsn1Integer_IsPresent(pRight))
+ {
+ if ( pLeft->Asn1Core.cb > 8
+ || pRight->Asn1Core.cb > 8)
+ {
+ uint32_t iLeft = RTAsn1Integer_UnsignedLastBit(pLeft);
+ uint32_t iRight = RTAsn1Integer_UnsignedLastBit(pRight);
+ if (iLeft != iRight)
+ return iLeft < iRight ? -1 : 1;
+ if ((int32_t)iLeft < 0)
+ return 0; /* Both are all zeros. */
+
+ uint32_t i = iLeft / 8;
+ if (i > 8)
+ {
+ uint8_t const *pbLeft = &pLeft->Asn1Core.uData.pu8[pLeft->Asn1Core.cb - i - 1];
+ uint8_t const *pbRight = &pRight->Asn1Core.uData.pu8[pRight->Asn1Core.cb - i - 1];
+ for (;;)
+ {
+ if (*pbLeft != *pbRight)
+ return *pbLeft < *pbRight ? -1 : 1;
+ if (--i <= 8)
+ break;
+ pbLeft++;
+ pbRight++;
+ }
+ }
+ }
+
+ if (pLeft->uValue.u == pRight->uValue.u)
+ iDiff = 0;
+ else
+ iDiff = pLeft->uValue.u < pRight->uValue.u ? -1 : 1;
+ }
+ else
+ iDiff = -1;
+ }
+ else
+ iDiff = 0 - (int)RTAsn1Integer_IsPresent(pRight);
+ return iDiff;
+}
+
+
+RTDECL(int) RTAsn1Integer_UnsignedCompareWithU64(PCRTASN1INTEGER pThis, uint64_t u64Const)
+{
+ int iDiff;
+ if (RTAsn1Integer_IsPresent(pThis))
+ {
+ if (pThis->Asn1Core.cb > 8)
+ {
+ int32_t iLast = RTAsn1Integer_UnsignedLastBit(pThis);
+ if (iLast >= 64)
+ return 1;
+ }
+
+ if (pThis->uValue.u == u64Const)
+ iDiff = 0;
+ else
+ iDiff = pThis->uValue.u < u64Const ? -1 : 1;
+ }
+ else
+ iDiff = 1;
+ return iDiff;
+}
+
+
+RTDECL(int) RTAsn1Integer_UnsignedCompareWithU32(PCRTASN1INTEGER pThis, uint32_t u32Const)
+{
+ int iDiff;
+ if (RTAsn1Integer_IsPresent(pThis))
+ {
+ if (pThis->Asn1Core.cb > 8)
+ {
+ int32_t iLast = RTAsn1Integer_UnsignedLastBit(pThis);
+ if (iLast >= 32)
+ return 1;
+ }
+
+ if (pThis->uValue.u == u32Const)
+ iDiff = 0;
+ else
+ iDiff = pThis->uValue.u < u32Const ? -1 : 1;
+ }
+ else
+ iDiff = 1;
+ return iDiff;
+}
+
+
+RTDECL(int) RTAsn1Integer_ToBigNum(PCRTASN1INTEGER pThis, PRTBIGNUM pBigNum, uint32_t fBigNumInit)
+{
+ AssertReturn(!(fBigNumInit & ~( RTBIGNUMINIT_F_SENSITIVE | RTBIGNUMINIT_F_UNSIGNED | RTBIGNUMINIT_F_SIGNED
+ | RTBIGNUMINIT_F_ENDIAN_LITTLE | RTBIGNUMINIT_F_ENDIAN_BIG)),
+ VERR_INVALID_PARAMETER);
+ AssertReturn(RTAsn1Integer_IsPresent(pThis), VERR_INVALID_PARAMETER);
+
+ if (!(fBigNumInit & (RTBIGNUMINIT_F_UNSIGNED | RTBIGNUMINIT_F_SIGNED)))
+ fBigNumInit |= RTBIGNUMINIT_F_SIGNED;
+
+ if (!(fBigNumInit & (RTBIGNUMINIT_F_ENDIAN_BIG | RTBIGNUMINIT_F_ENDIAN_LITTLE)))
+ fBigNumInit |= RTBIGNUMINIT_F_ENDIAN_BIG;
+
+ return RTBigNumInit(pBigNum, fBigNumInit, pThis->Asn1Core.uData.pv, pThis->Asn1Core.cb);
+}
+
+
+RTDECL(int) RTAsn1Integer_FromBigNum(PRTASN1INTEGER pThis, PCRTBIGNUM pBigNum, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ AssertPtr(pThis); AssertPtr(pBigNum); AssertPtr(pAllocator);
+
+ /* Be nice and auto init the object. */
+ if (!RTAsn1Integer_IsPresent(pThis))
+ RTAsn1Integer_Init(pThis, NULL);
+
+ uint32_t cb = RTBigNumByteWidth(pBigNum); Assert(cb > 0);
+ int rc = RTAsn1ContentReallocZ(&pThis->Asn1Core, cb, pAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(cb == pThis->Asn1Core.cb);
+ rc = RTBigNumToBytesBigEndian(pBigNum, (void *)pThis->Asn1Core.uData.pv, cb);
+ if (RT_SUCCESS(rc))
+ rtAsn1Integer_UpdateNativeValue(pThis);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1Integer_ToString(PCRTASN1INTEGER pThis, char *pszBuf, size_t cbBuf, uint32_t fFlags, size_t *pcbActual)
+{
+ AssertReturn(RTAsn1Integer_IsPresent(pThis), VERR_INVALID_PARAMETER);
+ AssertReturn(fFlags == 0, VERR_INVALID_FLAGS);
+
+ /*
+ * We only do hex conversions via this API.
+ * Currently we consider all numbers to be unsigned.
+ */
+ /** @todo Signed ASN.1 INTEGER. */
+ int rc;
+ size_t cbActual;
+ if (pThis->Asn1Core.cb <= 8)
+ {
+ cbActual = 2 + pThis->Asn1Core.cb*2 + 1;
+ if (cbActual <= cbBuf)
+ {
+ ssize_t cchFormat = RTStrFormatU64(pszBuf, cbBuf, pThis->uValue.u, 16, (int)cbActual - 1 /*cchWidth*/, 0,
+ RTSTR_F_SPECIAL | RTSTR_F_ZEROPAD);
+ rc = VINF_SUCCESS;
+ AssertStmt(cchFormat == (ssize_t)cbActual - 1, rc = VERR_INTERNAL_ERROR_3);
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ else
+ {
+ cbActual = pThis->Asn1Core.cb * 3 - 1 /* save one separator */ + 1 /* terminator */;
+ if (cbActual <= cbBuf)
+ {
+ rc = RTStrPrintHexBytes(pszBuf, cbBuf, pThis->Asn1Core.uData.pv, pThis->Asn1Core.cb, RTSTRPRINTHEXBYTES_F_SEP_SPACE);
+ Assert(rc == VINF_SUCCESS);
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ if (pcbActual)
+ *pcbActual = cbActual;
+ return rc;
+}
+
+
+/*
+ * ASN.1 INTEGER - Standard Methods.
+ */
+
+RT_DECL_DATA_CONST(RTASN1COREVTABLE const) g_RTAsn1Integer_Vtable =
+{
+ "RTAsn1Integer",
+ sizeof(RTASN1INTEGER),
+ ASN1_TAG_INTEGER,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ 0,
+ (PFNRTASN1COREVTDTOR)RTAsn1Integer_Delete,
+ NULL,
+ (PFNRTASN1COREVTCLONE)RTAsn1Integer_Clone,
+ (PFNRTASN1COREVTCOMPARE)RTAsn1Integer_Compare,
+ (PFNRTASN1COREVTCHECKSANITY)RTAsn1Integer_CheckSanity,
+ NULL,
+ NULL
+};
+
+
+RTDECL(int) RTAsn1Integer_Init(PRTASN1INTEGER pThis, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ RT_NOREF_PV(pAllocator);
+ RTAsn1Core_InitEx(&pThis->Asn1Core,
+ ASN1_TAG_INTEGER,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ &g_RTAsn1Integer_Vtable,
+ RTASN1CORE_F_PRESENT | RTASN1CORE_F_PRIMITE_TAG_STRUCT);
+ pThis->uValue.u = 1;
+ pThis->Asn1Core.cb = 1;
+ pThis->Asn1Core.uData.pv = (void *)&g_abSmall[0];
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1Integer_Clone(PRTASN1INTEGER pThis, PCRTASN1INTEGER pSrc, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ AssertPtr(pSrc); AssertPtr(pThis); AssertPtr(pAllocator);
+ RT_ZERO(*pThis);
+ if (RTAsn1Integer_IsPresent(pSrc))
+ {
+ AssertReturn(pSrc->Asn1Core.pOps == &g_RTAsn1Integer_Vtable, VERR_INTERNAL_ERROR_3);
+
+ int rc;
+ if ( pSrc->Asn1Core.cb != 1
+ || pSrc->uValue.u >= RT_ELEMENTS(g_abSmall))
+ {
+ /* Value is too large, copy it. */
+ rc = RTAsn1Core_CloneContent(&pThis->Asn1Core, &pSrc->Asn1Core, pAllocator);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ else
+ {
+ /* Use one of the const values. */
+ rc = RTAsn1Core_CloneNoContent(&pThis->Asn1Core, &pSrc->Asn1Core);
+ if (RT_FAILURE(rc))
+ return rc;
+ Assert(g_abSmall[pSrc->uValue.u] == pSrc->uValue.u);
+ pThis->Asn1Core.uData.pv = (void *)&g_abSmall[pSrc->uValue.u];
+ }
+ pThis->uValue.u = pSrc->uValue.u;
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(void) RTAsn1Integer_Delete(PRTASN1INTEGER pThis)
+{
+ if ( pThis
+ && RTAsn1Integer_IsPresent(pThis))
+ {
+ Assert(pThis->Asn1Core.pOps == &g_RTAsn1Integer_Vtable);
+
+ RTAsn1ContentFree(&pThis->Asn1Core);
+ RT_ZERO(*pThis);
+ }
+}
+
+
+RTDECL(int) RTAsn1Integer_Enum(PRTASN1INTEGER pThis, PFNRTASN1ENUMCALLBACK pfnCallback, uint32_t uDepth, void *pvUser)
+{
+ RT_NOREF_PV(pThis); RT_NOREF_PV(pfnCallback); RT_NOREF_PV(uDepth); RT_NOREF_PV(pvUser);
+ Assert(pThis && (!RTAsn1Integer_IsPresent(pThis) || pThis->Asn1Core.pOps == &g_RTAsn1Integer_Vtable));
+
+ /* No children to enumerate. */
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1Integer_Compare(PCRTASN1INTEGER pLeft, PCRTASN1INTEGER pRight)
+{
+ return RTAsn1Integer_UnsignedCompare(pLeft, pRight);
+}
+
+
+RTDECL(int) RTAsn1Integer_CheckSanity(PCRTASN1INTEGER pThis, uint32_t fFlags, PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ RT_NOREF_PV(fFlags);
+ if (RT_UNLIKELY(!RTAsn1Integer_IsPresent(pThis)))
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_NOT_PRESENT, "%s: Missing (INTEGER).", pszErrorTag);
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * Generate code for the associated collection types.
+ */
+#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-integer-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+#include <iprt/asn1-generator-core.h>
+#include <iprt/asn1-generator-init.h>
+#include <iprt/asn1-generator-sanity.h>
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-null-decode.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-null-decode.cpp
new file mode 100644
index 00000000..5c43df1b
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-null-decode.cpp
@@ -0,0 +1,73 @@
+/* $Id: asn1-ut-null-decode.cpp $ */
+/** @file
+ * IPRT - ASN.1, NULL type, Decoding.
+ */
+
+/*
+ * 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/err.h>
+#include <iprt/string.h>
+
+#include <iprt/formats/asn1.h>
+
+
+RTDECL(int) RTAsn1Null_DecodeAsn1(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1NULL pThis, const char *pszErrorTag)
+{
+ int rc = RTAsn1CursorReadHdr(pCursor, &pThis->Asn1Core, pszErrorTag);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTAsn1CursorMatchTagClassFlags(pCursor, &pThis->Asn1Core, ASN1_TAG_NULL,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE, fFlags, pszErrorTag, "NULL");
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->Asn1Core.cb == 0)
+ {
+ pThis->Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
+ pThis->Asn1Core.pOps = &g_RTAsn1Null_Vtable;
+ return VINF_SUCCESS;
+ }
+
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_NULL_ENCODING,
+ "%s: Expected NULL object to have zero length: %#x", pszErrorTag, pThis->Asn1Core.cb);
+ }
+ }
+ RT_ZERO(*pThis);
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-null.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-null.cpp
new file mode 100644
index 00000000..9828dc51
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-null.cpp
@@ -0,0 +1,141 @@
+/* $Id: asn1-ut-null.cpp $ */
+/** @file
+ * IPRT - ASN.1, NULL type.
+ */
+
+/*
+ * 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/err.h>
+#include <iprt/string.h>
+
+#include <iprt/formats/asn1.h>
+
+
+/*
+ * ASN.1 NULL - Special Methods.
+ */
+
+
+/*
+ * ASN.1 NULL - Standard Methods.
+ */
+
+
+RT_DECL_DATA_CONST(RTASN1COREVTABLE const) g_RTAsn1Null_Vtable =
+{
+ "RTAsn1Null",
+ sizeof(RTASN1NULL),
+ ASN1_TAG_NULL,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ 0,
+ (PFNRTASN1COREVTDTOR)RTAsn1Null_Delete,
+ (PFNRTASN1COREVTENUM)RTAsn1Null_Enum,
+ (PFNRTASN1COREVTCLONE)RTAsn1Null_Clone,
+ (PFNRTASN1COREVTCOMPARE)RTAsn1Null_Compare,
+ (PFNRTASN1COREVTCHECKSANITY)RTAsn1Null_CheckSanity,
+ NULL,
+ NULL
+};
+
+
+RTDECL(int) RTAsn1Null_Init(PRTASN1NULL pThis, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ RT_NOREF_PV(pAllocator);
+ return RTAsn1Core_InitEx(&pThis->Asn1Core, ASN1_TAG_NULL, ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ &g_RTAsn1Null_Vtable, RTASN1CORE_F_PRESENT | RTASN1CORE_F_PRIMITE_TAG_STRUCT);
+}
+
+
+RTDECL(int) RTAsn1Null_Clone(PRTASN1NULL pThis, PCRTASN1NULL pSrc, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ AssertPtr(pSrc); AssertPtr(pThis); AssertPtr(pAllocator); RT_NOREF_PV(pAllocator);
+ RT_ZERO(*pThis);
+ if (RTAsn1Null_IsPresent(pSrc))
+ {
+ AssertReturn(pSrc->Asn1Core.pOps == &g_RTAsn1Null_Vtable, VERR_INTERNAL_ERROR_3);
+ AssertReturn(pSrc->Asn1Core.cb == 0, VERR_INTERNAL_ERROR_4);
+
+ int rc = RTAsn1Core_CloneNoContent(&pThis->Asn1Core, &pSrc->Asn1Core);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(void) RTAsn1Null_Delete(PRTASN1NULL pThis)
+{
+ if ( pThis
+ && RTAsn1Null_IsPresent(pThis))
+ {
+ Assert(pThis->Asn1Core.pOps == &g_RTAsn1Null_Vtable);
+ RT_ZERO(*pThis);
+ }
+}
+
+
+RTDECL(int) RTAsn1Null_Enum(PRTASN1NULL pThis, PFNRTASN1ENUMCALLBACK pfnCallback, uint32_t uDepth, void *pvUser)
+{
+ RT_NOREF_PV(pThis); RT_NOREF_PV(pfnCallback); RT_NOREF_PV(uDepth); RT_NOREF_PV(pvUser);
+ Assert(pThis && (!RTAsn1Null_IsPresent(pThis) || pThis->Asn1Core.pOps == &g_RTAsn1Null_Vtable));
+
+ /* No children to enumerate. */
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1Null_Compare(PCRTASN1NULL pLeft, PCRTASN1NULL pRight)
+{
+ Assert(pLeft && (!RTAsn1Null_IsPresent(pLeft) || pLeft->Asn1Core.pOps == &g_RTAsn1Null_Vtable));
+ Assert(pRight && (!RTAsn1Null_IsPresent(pRight) || pRight->Asn1Core.pOps == &g_RTAsn1Null_Vtable));
+
+ return (int)RTAsn1Null_IsPresent(pLeft) - (int)RTAsn1Null_IsPresent(pRight);
+}
+
+
+RTDECL(int) RTAsn1Null_CheckSanity(PCRTASN1NULL pThis, uint32_t fFlags, PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ RT_NOREF_PV(fFlags);
+ if (RT_UNLIKELY(!RTAsn1Null_IsPresent(pThis)))
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_NOT_PRESENT, "%s: Missing (NULL).", pszErrorTag);
+ return VINF_SUCCESS;
+}
+
+/* No NULL object collections. */
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-objid-decode.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-objid-decode.cpp
new file mode 100644
index 00000000..6d0e66a0
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-objid-decode.cpp
@@ -0,0 +1,377 @@
+/* $Id: asn1-ut-objid-decode.cpp $ */
+/** @file
+ * IPRT - ASN.1, OBJECT IDENTIFIER Type, Decoder.
+ */
+
+/*
+ * 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/err.h>
+#include <iprt/string.h>
+#include <iprt/ctype.h>
+
+#include <iprt/formats/asn1.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static char const g_achDigits[11] = "0123456789";
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+DECLHIDDEN(int) rtAsn1ObjId_InternalFormatComponent(uint32_t uValue, char **ppszObjId, size_t *pcbObjId); /* asn1-ut-objid.cpp */
+
+
+/**
+ * Internal worker for RTAsn1ObjId_DecodeAsn1 that formats a component, with a
+ * leading dot.
+ *
+ * @returns VBox status code (caller complains on failure).
+ * @param uValue The component ID value.
+ * @param ppszObjId Pointer to the output buffer pointer.
+ * @param pcbObjId Pointer to the remaining size of the output buffer.
+ */
+DECLHIDDEN(int) rtAsn1ObjId_InternalFormatComponent(uint32_t uValue, char **ppszObjId, size_t *pcbObjId)
+{
+ /*
+ * Format the number backwards.
+ */
+ char szTmp[32];
+ char *psz = &szTmp[sizeof(szTmp) - 1];
+ *psz = '\0';
+
+ do
+ {
+ *--psz = g_achDigits[uValue % 10];
+ uValue /= 10;
+ } while (uValue > 0);
+
+ /*
+ * Do we have enough space?
+ * We add a dot and save space for the terminator.
+ */
+ size_t cchNeeded = &szTmp[sizeof(szTmp) - 1] - psz;
+ if (1 + cchNeeded < *pcbObjId)
+ {
+ *pcbObjId -= cchNeeded + 1;
+ char *pszObjId = *ppszObjId;
+ *ppszObjId = pszObjId + cchNeeded + 1;
+
+ *pszObjId = '.';
+ memcpy(pszObjId + 1, psz, cchNeeded);
+ return VINF_SUCCESS;
+ }
+
+ AssertFailed();
+ return VERR_ASN1_OBJID_TOO_LONG_STRING_FORM;
+}
+
+
+/**
+ * Reads one object ID component, returning it's value and encoded length.
+ *
+ * @returns The encoded length (positive) on success, negative IPRT status code
+ * on failure.
+ * @param pbContent The start of the component to parse.
+ * @param cbContent The number of content bytes left.
+ * @param puValue Where to return the value.
+ */
+static int rtAsn1ObjId_ReadComponent(uint8_t const *pbContent, uint32_t cbContent, uint32_t *puValue)
+{
+ if (cbContent >= 1)
+ {
+ /* The first byte. */
+ uint8_t b = *pbContent;
+ if (!(b & 0x80))
+ {
+ *puValue = b;
+ return 1;
+ }
+
+ /* Encoded as more than one byte. Make sure that it's efficently
+ encoded as 8.19.2 indicates it must. */
+ if (b != 0x80)
+ {
+ uint32_t off = 1;
+ uint32_t uValue = b & 0x7f;
+ while (off < cbContent)
+ {
+ b = pbContent[off++];
+ uValue <<= 7;
+ uValue |= b & 0x7f;
+ if (!(b & 0x80))
+ {
+ *puValue = uValue;
+ return (int)off;
+ }
+
+ if (RT_UNLIKELY(uValue & UINT32_C(0x0e000000)))
+ return VERR_ASN1_OBJID_COMPONENT_TOO_BIG;
+ }
+ }
+ return VERR_ASN1_INVALID_OBJID_ENCODING;
+ }
+ return VERR_NO_DATA;
+}
+
+
+/**
+ * This function parses the binary content of an OBJECT IDENTIFIER, check the
+ * encoding as well as calculating the storage requirements.
+ *
+ * @returns IPRT status code
+ * @param pbContent Pointer to the content.
+ * @param cbContent The length of the content.
+ * @param pCursor The cursor (for error reporting).
+ * @param pszErrorTag The error tag.
+ * @param pcComponents Where to return the component count.
+ * @param pcchObjId Where to return the length of the dotted string
+ * representation.
+ */
+static int rtAsn1ObjId_PreParse(uint8_t const *pbContent, uint32_t cbContent, PRTASN1CURSOR pCursor, const char *pszErrorTag,
+ uint8_t *pcComponents, uint8_t *pcchObjId)
+{
+ int rc;
+ if (cbContent >= 1 && cbContent < _1K)
+ {
+ /*
+ * Decode the first two numbers. Monkey business: X*40 + Y
+ * Where X is the first number, X in {0,1,2}, and Y is the second
+ * one. The range of Y is {0,...,39} for X in {0,1}, but has a
+ * free range for X = 2.
+ */
+ uint32_t cComponents = 1;
+ uint32_t uValue;
+ rc = rtAsn1ObjId_ReadComponent(pbContent, cbContent, &uValue);
+ if (rc > 0)
+ {
+ uint32_t cchObjId = 1;
+ uValue = uValue < 2*40 ? uValue % 40 : uValue - 2*40; /* Y */
+ do
+ {
+ cComponents++;
+
+ /* Figure the encoded string length, binary search fashion. */
+ if (uValue < 10000)
+ {
+ if (uValue < 100)
+ {
+ if (uValue < 10)
+ cchObjId += 1 + 1;
+ else
+ cchObjId += 1 + 2;
+ }
+ else
+ {
+ if (uValue < 1000)
+ cchObjId += 1 + 3;
+ else
+ cchObjId += 1 + 4;
+ }
+ }
+ else
+ {
+ if (uValue < 1000000)
+ {
+ if (uValue < 100000)
+ cchObjId += 1 + 5;
+ else
+ cchObjId += 1 + 6;
+ }
+ else
+ {
+ if (uValue < 10000000)
+ cchObjId += 1 + 7;
+ else if (uValue < 100000000)
+ cchObjId += 1 + 8;
+ else
+ cchObjId += 1 + 9;
+ }
+ }
+
+ /* advance. */
+ pbContent += rc;
+ cbContent -= rc;
+ if (!cbContent)
+ {
+ if (cComponents < 128)
+ {
+ if (cchObjId < RT_SIZEOFMEMB(RTASN1OBJID, szObjId))
+ {
+ *pcComponents = cComponents;
+ *pcchObjId = cchObjId;
+ return VINF_SUCCESS;
+ }
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_OBJID_TOO_LONG_STRING_FORM,
+ "%s: Object ID has a too long string form: %#x (max %#x)",
+ pszErrorTag, cchObjId, RT_SIZEOFMEMB(RTASN1OBJID, szObjId));
+ }
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_OBJID_TOO_MANY_COMPONENTS,
+ "%s: Object ID has too many components: %#x (max 127)", pszErrorTag, cComponents);
+ }
+
+ /* next */
+ rc = rtAsn1ObjId_ReadComponent(pbContent, cbContent, &uValue);
+ } while (rc > 0);
+ }
+ rc = RTAsn1CursorSetInfo(pCursor, rc, "%s: Bad object ID component #%u encoding: %.*Rhxs",
+ pszErrorTag, cComponents, cbContent, pbContent);
+ }
+ else if (cbContent)
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_OBJID_ENCODING, "%s: Object ID content is loo long: %#x",
+ pszErrorTag, cbContent);
+ else
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_OBJID_ENCODING, "%s: Zero length object ID content", pszErrorTag);
+ return rc;
+}
+
+
+
+RTDECL(int) RTAsn1ObjId_DecodeAsn1(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1OBJID pThis, const char *pszErrorTag)
+{
+ int rc = RTAsn1CursorReadHdr(pCursor, &pThis->Asn1Core, pszErrorTag);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTAsn1CursorMatchTagClassFlags(pCursor, &pThis->Asn1Core, ASN1_TAG_OID,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE, fFlags, pszErrorTag, "OID");
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Validate and count things first.
+ */
+ uint8_t cComponents = 0; /* gcc maybe-crap */
+ uint8_t cchObjId = 0; /* ditto */
+ rc = rtAsn1ObjId_PreParse(pCursor->pbCur, pThis->Asn1Core.cb, pCursor, pszErrorTag, &cComponents, &cchObjId);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Allocate memory for the components array, either out of the
+ * string buffer or off the heap.
+ */
+ pThis->cComponents = cComponents;
+ RTAsn1CursorInitAllocation(pCursor, &pThis->Allocation);
+#if 0 /** @todo breaks with arrays of ObjIds or structs containing them. They get resized and repositioned in memory, thus invalidating the pointer. Add recall-pointers callback, or just waste memory? Or maybe make all arrays pointer-arrays? */
+ if (cComponents * sizeof(uint32_t) <= sizeof(pThis->szObjId) - cchObjId - 1)
+ pThis->pauComponents = (uint32_t *)&pThis->szObjId[sizeof(pThis->szObjId) - cComponents * sizeof(uint32_t)];
+ else
+#endif
+ rc = RTAsn1MemAllocZ(&pThis->Allocation, (void **)&pThis->pauComponents,
+ cComponents * sizeof(pThis->pauComponents[0]));
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t *pauComponents = (uint32_t *)pThis->pauComponents;
+
+ /*
+ * Deal with the two first components first since they are
+ * encoded in a weird way to save a byte.
+ */
+ uint8_t const *pbContent = pCursor->pbCur;
+ uint32_t cbContent = pThis->Asn1Core.cb;
+ uint32_t uValue;
+ rc = rtAsn1ObjId_ReadComponent(pbContent, cbContent, &uValue); AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ pbContent += rc;
+ cbContent -= rc;
+
+ if (uValue < 80)
+ {
+ pauComponents[0] = uValue / 40;
+ pauComponents[1] = uValue % 40;
+ }
+ else
+ {
+ pauComponents[0] = 2;
+ pauComponents[1] = uValue - 2*40;
+ }
+
+ char *pszObjId = &pThis->szObjId[0];
+ *pszObjId++ = g_achDigits[pauComponents[0]];
+ size_t cbObjIdLeft = cchObjId + 1 - 1;
+
+ rc = rtAsn1ObjId_InternalFormatComponent(pauComponents[1], &pszObjId, &cbObjIdLeft); AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * The other components are encoded in less complicated manner.
+ */
+ for (uint32_t i = 2; i < cComponents; i++)
+ {
+ rc = rtAsn1ObjId_ReadComponent(pbContent, cbContent, &uValue);
+ AssertRCBreak(rc);
+ pbContent += rc;
+ cbContent -= rc;
+ pauComponents[i] = uValue;
+ rc = rtAsn1ObjId_InternalFormatComponent(uValue, &pszObjId, &cbObjIdLeft);
+ AssertRCBreak(rc);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ Assert(cbObjIdLeft == 1);
+ *pszObjId = '\0';
+
+ RTAsn1CursorSkip(pCursor, pThis->Asn1Core.cb);
+ pThis->Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
+ pThis->Asn1Core.pOps = &g_RTAsn1ObjId_Vtable;
+ return VINF_SUCCESS;
+ }
+ }
+ }
+ RTAsn1MemFree(&pThis->Allocation, (void *)pThis->pauComponents);
+ pThis->pauComponents = NULL;
+ }
+ }
+ }
+ }
+ RT_ZERO(*pThis);
+ return rc;
+}
+
+
+
+/*
+ * Generate code for the associated collection types.
+ */
+#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-objid-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+#include <iprt/asn1-generator-asn1-decoder.h>
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-objid-template.h b/src/VBox/Runtime/common/asn1/asn1-ut-objid-template.h
new file mode 100644
index 00000000..102aae15
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-objid-template.h
@@ -0,0 +1,64 @@
+/* $Id: asn1-ut-objid-template.h $ */
+/** @file
+ * IPRT - ASN.1, OBJECT IDENTIFIER Type, Collection Type Template.
+ */
+
+/*
+ * 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
+ */
+
+#define RTASN1TMPL_DECL RTDECL
+
+#define RTASN1TMPL_TYPE RTASN1SEQOFOBJIDS
+#define RTASN1TMPL_EXT_NAME RTAsn1SeqOfObjIds
+#define RTASN1TMPL_INT_NAME rtAsn1SeqOfObjIds
+RTASN1TMPL_SEQ_OF(RTASN1OBJID, RTAsn1ObjId);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+#define RTASN1TMPL_TYPE RTASN1SETOFOBJIDS
+#define RTASN1TMPL_EXT_NAME RTAsn1SetOfObjIds
+#define RTASN1TMPL_INT_NAME rtAsn1SetOfObjIds
+RTASN1TMPL_SET_OF(RTASN1OBJID, RTAsn1ObjId);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+#define RTASN1TMPL_TYPE RTASN1SETOFOBJIDSEQS
+#define RTASN1TMPL_EXT_NAME RTAsn1SetOfObjIdSeqs
+#define RTASN1TMPL_INT_NAME rtAsn1SetOfObjIdSeqs
+RTASN1TMPL_SET_OF(RTASN1SEQOFOBJIDS, RTAsn1SeqOfObjIds);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-objid.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-objid.cpp
new file mode 100644
index 00000000..d1212437
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-objid.cpp
@@ -0,0 +1,566 @@
+/* $Id: asn1-ut-objid.cpp $ */
+/** @file
+ * IPRT - ASN.1, OBJECT IDENTIFIER Type.
+ */
+
+/*
+ * 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>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static char const g_szDefault[] = "2.16.840.1.113894";
+static uint32_t const g_auDefault[] = { 2, 16, 840, 1, 113894 };
+static uint8_t const g_abDefault[] =
+{
+ 2*40 + 16, 0x80 | (840 >> 7), 840 & 0x7f, 1, 0x80 | (113894 >> 14), 0x80 | ((113894 >> 7) & 0x7f), 113894 & 0x7f
+};
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+DECLHIDDEN(int) rtAsn1ObjId_InternalFormatComponent(uint32_t uValue, char **ppszObjId, size_t *pcbObjId); /* asn1-ut-objid.cpp */
+/** @todo check if we really need this. */
+
+
+
+/*
+ * ASN.1 OBJECT IDENTIFIER - Special Methods.
+ */
+
+/**
+ * Encodes the ASN.1 byte sequence for a set of components.
+ *
+ * @returns IPRT status code.
+ * @param cComponents The number of components. Must be at least two.
+ * @param pauComponents The components array.
+ * @param pbEncoded The output buffer.
+ * @param pcbEncoded On input, this holds the size of the output buffer.
+ * On successful return it's the encoded size in bytes.
+ */
+static int rtAsn1ObjId_EncodeComponents(uint32_t cComponents, uint32_t const *pauComponents,
+ uint8_t *pbEncoded, uint32_t *pcbEncoded)
+{
+ uint8_t *pbCur = pbEncoded;
+ uint32_t cbLeft = *pcbEncoded;
+
+ /* The first two componets are encoded together to save a byte, so the loop
+ organization is a little special. */
+ AssertReturn(cComponents >= 2, VERR_ASN1_INTERNAL_ERROR_1);
+ AssertReturn(pauComponents[0] <= 2, VERR_ASN1_INTERNAL_ERROR_1);
+ AssertReturn(pauComponents[1] <= (pauComponents[0] < 2 ? 39 : UINT32_MAX - 80), VERR_ASN1_INTERNAL_ERROR_1);
+ uint32_t i = 1;
+ uint32_t uValue = pauComponents[0] * 40 + pauComponents[1];
+
+ for (;;)
+ {
+ if (uValue < 0x80)
+ {
+ if (RT_UNLIKELY(cbLeft < 1))
+ return VERR_BUFFER_OVERFLOW;
+ cbLeft -= 1;
+ *pbCur++ = (uint8_t)uValue;
+ }
+ else if (uValue < 0x4000)
+ {
+ if (RT_UNLIKELY(cbLeft < 2))
+ return VERR_BUFFER_OVERFLOW;
+ cbLeft -= 2;
+ pbCur[0] = (uValue >> 7) | 0x80;
+ pbCur[1] = uValue & 0x7f;
+ pbCur += 2;
+ }
+ else if (uValue < 0x200000)
+ {
+ if (RT_UNLIKELY(cbLeft < 3))
+ return VERR_BUFFER_OVERFLOW;
+ cbLeft -= 3;
+ pbCur[0] = (uValue >> 14) | 0x80;
+ pbCur[1] = ((uValue >> 7) & 0x7f) | 0x80;
+ pbCur[2] = uValue & 0x7f;
+ pbCur += 3;
+ }
+ else if (uValue < 0x10000000)
+ {
+ if (RT_UNLIKELY(cbLeft < 4))
+ return VERR_BUFFER_OVERFLOW;
+ cbLeft -= 4;
+ pbCur[0] = (uValue >> 21) | 0x80;
+ pbCur[1] = ((uValue >> 14) & 0x7f) | 0x80;
+ pbCur[2] = ((uValue >> 7) & 0x7f) | 0x80;
+ pbCur[3] = uValue & 0x7f;
+ pbCur += 4;
+ }
+ else
+ {
+ if (RT_UNLIKELY(cbLeft < 5))
+ return VERR_BUFFER_OVERFLOW;
+ cbLeft -= 5;
+ pbCur[0] = (uValue >> 28) | 0x80;
+ pbCur[1] = ((uValue >> 21) & 0x7f) | 0x80;
+ pbCur[2] = ((uValue >> 14) & 0x7f) | 0x80;
+ pbCur[3] = ((uValue >> 7) & 0x7f) | 0x80;
+ pbCur[4] = uValue & 0x7f;
+ pbCur += 5;
+ }
+
+ /* Advance / return. */
+ i++;
+ if (i >= cComponents)
+ {
+ *pcbEncoded = (uint32_t)(pbCur - pbEncoded);
+ return VINF_SUCCESS;
+ }
+ uValue = pauComponents[i];
+ }
+}
+
+
+RTDECL(int) RTAsn1ObjId_InitFromString(PRTASN1OBJID pThis, const char *pszObjId, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ RT_ZERO(*pThis);
+
+ /*
+ * Check the string, counting the number of components and checking their validity.
+ */
+ size_t cbObjId = strlen(pszObjId) + 1;
+ AssertReturn(cbObjId < sizeof(pThis->szObjId), VERR_ASN1_OBJID_TOO_LONG_STRING_FORM);
+
+ const char *psz = pszObjId;
+
+ /* Special checking of the first component. It has only three valid values: 0,1,2. */
+ char ch = *psz++;
+ if (RT_UNLIKELY(ch < '0' || ch > '2'))
+ return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
+ char const chFirst = ch;
+ ch = *psz++;
+ if (RT_UNLIKELY(ch != '.'))
+ return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
+
+ /* The 2nd component. It the first is 0 or 1, it has a max of 39. */
+ uint32_t cComponents = 1;
+ if (chFirst < '2')
+ {
+ ch = *psz++;
+ if (*psz == '.')
+ {
+ if (RT_UNLIKELY(!RT_C_IS_DIGIT(ch)))
+ return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
+ }
+ else
+ {
+ if (RT_UNLIKELY(ch < '0' || ch > '3'))
+ return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
+ ch = *psz++;
+ if (RT_UNLIKELY(!RT_C_IS_DIGIT(ch)))
+ return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
+ if (*psz != '.')
+ return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
+ }
+ cComponents++;
+ }
+ else
+ psz--;
+
+ /* Subsequent components have max values of UINT32_MAX - 80. */
+ while ((ch = *psz++) != '\0')
+ {
+ if (RT_UNLIKELY(ch != '.'))
+ return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
+ const char *pszStart = psz;
+
+ /* Special treatment of the first digit. Need to make sure it isn't an
+ unnecessary leading 0. */
+ ch = *psz++;
+ if (RT_UNLIKELY(!RT_C_IS_DIGIT(ch)))
+ return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
+ if (RT_UNLIKELY(ch == '0' && RT_C_IS_DIGIT(*psz)))
+ return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
+
+ /* The rest of the digits. */
+ while ((ch = *psz) != '.' && ch != '\0')
+ {
+ if (RT_UNLIKELY(!RT_C_IS_DIGIT(ch)))
+ return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
+ psz++;
+ }
+
+ /* Check the value range. */
+ if (RT_UNLIKELY(psz - pszStart >= 9))
+ if ( psz - pszStart > 9
+ || strncmp(pszStart, "4294967216", 9) >= 0) /* 2^32 - 80 */
+ return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
+
+ cComponents++;
+ }
+
+ if (RT_UNLIKELY(cComponents >= 128))
+ return VERR_ASN1_OBJID_TOO_MANY_COMPONENTS;
+ pThis->cComponents = (uint8_t)cComponents;
+
+ /*
+ * Find space for the component array, either at the unused end of szObjId
+ * or on the heap.
+ */
+ int rc;
+ RTAsn1MemInitAllocation(&pThis->Allocation, pAllocator);
+#if 0 /** @todo breaks with arrays of ObjIds or structs containing them. They get resized and repositioned in memory, thus invalidating the pointer. Add recall-pointers callback, or just waste memory? Or maybe make all arrays pointer-arrays? */
+ size_t cbLeft = sizeof(pThis->szObjId) - cbObjId;
+ if (cbLeft >= cComponents * sizeof(uint32_t))
+ {
+ pThis->pauComponents = (uint32_t *)&pThis->szObjId[sizeof(pThis->szObjId) - cComponents * sizeof(uint32_t)];
+ cbLeft -= cComponents * sizeof(uint32_t);
+ rc = VINF_SUCCESS;
+ }
+ else
+#endif
+ rc = RTAsn1MemAllocZ(&pThis->Allocation, (void **)&pThis->pauComponents, cComponents * sizeof(uint32_t));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Fill the elements array.
+ */
+ uint32_t *pauComponents = (uint32_t *)pThis->pauComponents;
+ rc = VINF_SUCCESS;
+ psz = pszObjId;
+ for (uint32_t i = 0; i < cComponents; i++)
+ {
+ uint32_t uValue = 0;
+ rc = RTStrToUInt32Ex(psz, (char **)&psz, 10, &uValue);
+ if (rc == VWRN_TRAILING_CHARS)
+ {
+ pauComponents[i] = uValue;
+ AssertBreakStmt(*psz == '.', rc = VERR_TRAILING_CHARS);
+ psz++;
+ }
+ else if (rc == VINF_SUCCESS)
+ {
+ pauComponents[i] = uValue;
+ Assert(*psz == '\0');
+ }
+ else if (RT_FAILURE(rc))
+ break;
+ else
+ {
+ rc = -rc;
+ break;
+ }
+ }
+ if (rc == VINF_SUCCESS && *psz == '\0')
+ {
+ /*
+ * Initialize the core structure before we start on the encoded bytes.
+ */
+ RTAsn1Core_InitEx(&pThis->Asn1Core,
+ ASN1_TAG_OID,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ &g_RTAsn1ObjId_Vtable,
+ RTASN1CORE_F_PRESENT | RTASN1CORE_F_PRIMITE_TAG_STRUCT);
+
+ /*
+ * Encode the value into the string buffer. This will NOT overflow
+ * because the string representation is much less efficient than the
+ * binary ASN.1 representation (base-10 + separators vs. base-128).
+ */
+ pThis->Asn1Core.cb = (uint32_t)cbObjId;
+ rc = rtAsn1ObjId_EncodeComponents(cComponents, pThis->pauComponents,
+ (uint8_t *)&pThis->szObjId[0], &pThis->Asn1Core.cb);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Now, find a place for the encoded bytes. There might be
+ * enough room left in the szObjId for it if we're lucky.
+ */
+#if 0 /** @todo breaks with arrays of ObjIds or structs containing them. They get resized and repositioned in memory, thus invalidating the pointer. Add recall-pointers callback, or just waste memory? Or maybe make all arrays pointer-arrays? */
+ if (pThis->Asn1Core.cb >= cbLeft)
+ pThis->Asn1Core.uData.pv = memmove(&pThis->szObjId[cbObjId], &pThis->szObjId[0], pThis->Asn1Core.cb);
+ else
+#endif
+ rc = RTAsn1ContentDup(&pThis->Asn1Core, pThis->szObjId, pThis->Asn1Core.cb, pAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Finally, copy the dotted string.
+ */
+ memcpy(pThis->szObjId, pszObjId, cbObjId);
+ return VINF_SUCCESS;
+ }
+ }
+ else
+ {
+ AssertMsgFailed(("%Rrc\n", rc));
+ rc = VERR_ASN1_INTERNAL_ERROR_3;
+ }
+ }
+ else
+ rc = VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
+ }
+ RT_ZERO(*pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1ObjId_SetFromString(PRTASN1OBJID pThis, const char *pszObjId, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ RTAsn1ObjId_Delete(pThis);
+ int rc = RTAsn1ObjId_InitFromString(pThis, pszObjId, pAllocator);
+ if (RT_FAILURE(rc))
+ RTAsn1ObjId_Init(pThis, pAllocator);
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1ObjId_CompareWithString(PCRTASN1OBJID pThis, const char *pszRight)
+{
+ return strcmp(pThis->szObjId, pszRight);
+}
+
+
+RTDECL(bool) RTAsn1ObjId_StartsWith(PCRTASN1OBJID pThis, const char *pszStartsWith)
+{
+ size_t cchStartsWith = strlen(pszStartsWith);
+ return !strncmp(pThis->szObjId, pszStartsWith, cchStartsWith)
+ && ( pszStartsWith[cchStartsWith] == '.'
+ || pszStartsWith[cchStartsWith] == '\0');
+}
+
+
+RTDECL(uint8_t) RTAsn1ObjIdCountComponents(PCRTASN1OBJID pThis)
+{
+ return pThis->cComponents;
+}
+
+
+RTDECL(uint32_t) RTAsn1ObjIdGetComponentsAsUInt32(PCRTASN1OBJID pThis, uint8_t iComponent)
+{
+ if (iComponent < pThis->cComponents)
+ return pThis->pauComponents[iComponent];
+ return UINT32_MAX;
+}
+
+
+RTDECL(uint32_t) RTAsn1ObjIdGetLastComponentsAsUInt32(PCRTASN1OBJID pThis)
+{
+ return pThis->pauComponents[pThis->cComponents - 1];
+}
+
+
+/*
+ * ASN.1 OBJECT IDENTIFIER - Standard Methods.
+ */
+
+RT_DECL_DATA_CONST(RTASN1COREVTABLE const) g_RTAsn1ObjId_Vtable =
+{
+ "RTAsn1ObjId",
+ sizeof(RTASN1OBJID),
+ ASN1_TAG_OID,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ 0,
+ (PFNRTASN1COREVTDTOR)RTAsn1ObjId_Delete,
+ NULL,
+ (PFNRTASN1COREVTCLONE)RTAsn1ObjId_Clone,
+ (PFNRTASN1COREVTCOMPARE)RTAsn1ObjId_Compare,
+ (PFNRTASN1COREVTCHECKSANITY)RTAsn1ObjId_CheckSanity,
+ NULL,
+ NULL
+};
+
+
+RTDECL(int) RTAsn1ObjId_Init(PRTASN1OBJID pThis, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ RT_NOREF_PV(pAllocator);
+ RTAsn1Core_InitEx(&pThis->Asn1Core,
+ ASN1_TAG_OID,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ &g_RTAsn1ObjId_Vtable,
+ RTASN1CORE_F_PRESENT | RTASN1CORE_F_PRIMITE_TAG_STRUCT);
+ pThis->Asn1Core.cb = sizeof(g_abDefault);
+ pThis->Asn1Core.uData.pv = (void *)&g_abDefault[0];
+ pThis->cComponents = RT_ELEMENTS(g_auDefault);
+ pThis->pauComponents = g_auDefault;
+ AssertCompile(sizeof(g_szDefault) <= sizeof(pThis->szObjId));
+ memcpy(pThis->szObjId, g_szDefault, sizeof(g_szDefault));
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1ObjId_Clone(PRTASN1OBJID pThis, PCRTASN1OBJID pSrc, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ AssertPtr(pSrc); AssertPtr(pThis); AssertPtr(pAllocator);
+ RT_ZERO(*pThis);
+ if (RTAsn1ObjId_IsPresent(pSrc))
+ {
+ AssertReturn(pSrc->Asn1Core.pOps == &g_RTAsn1ObjId_Vtable, VERR_INTERNAL_ERROR_3);
+
+ /* Copy the dotted string representation. */
+ size_t cbObjId = strlen(pSrc->szObjId) + 1;
+ AssertReturn(cbObjId <= sizeof(pThis->szObjId), VERR_INTERNAL_ERROR_5);
+ memcpy(pThis->szObjId, pSrc->szObjId, cbObjId);
+
+ /* Copy the integer component array. Try fit it in the unused space of
+ the dotted object string buffer. We place it at the end of the
+ buffer as that is simple alignment wise and avoid wasting bytes that
+ could be used to sequueze in the content bytes (see below). */
+ int rc;
+ RTAsn1MemInitAllocation(&pThis->Allocation, pAllocator);
+ pThis->cComponents = pSrc->cComponents;
+#if 0 /** @todo breaks with arrays of ObjIds or structs containing them. They get resized and repositioned in memory, thus invalidating the pointer. Add recall-pointers callback, or just waste memory? Or maybe make all arrays pointer-arrays? */
+ size_t cbLeft = sizeof(pThis->szObjId);
+ if (pSrc->cComponents * sizeof(uint32_t) <= cbLeft)
+ {
+ pThis->pauComponents = (uint32_t *)&pThis->szObjId[sizeof(pThis->szObjId) - pSrc->cComponents * sizeof(uint32_t)];
+ memcpy((uint32_t *)pThis->pauComponents, pSrc->pauComponents, pSrc->cComponents * sizeof(uint32_t));
+ cbLeft -= pSrc->cComponents * sizeof(uint32_t);
+ rc = VINF_SUCCESS;
+ }
+ else
+#endif
+ {
+ rc = RTAsn1MemDup(&pThis->Allocation, (void **)&pThis->pauComponents, pSrc->pauComponents,
+ pSrc->cComponents * sizeof(uint32_t));
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /* See if we can fit the content value into the szObjId as well.
+ It will follow immediately after the string as the component
+ array is the end of the string buffer, when present. */
+#if 0 /** @todo breaks with arrays of ObjIds or structs containing them. They get resized and repositioned in memory, thus invalidating the pointer. Add recall-pointers callback, or just waste memory? Or maybe make all arrays pointer-arrays? */
+ uint32_t cbContent = pSrc->Asn1Core.cb;
+ if (cbContent <= cbLeft)
+ {
+ rc = RTAsn1Core_CloneNoContent(&pThis->Asn1Core, &pSrc->Asn1Core);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->Asn1Core.uData.pv = memcpy(&pThis->szObjId[cbObjId], pSrc->Asn1Core.uData.pv, cbContent);
+ return VINF_SUCCESS;
+ }
+ }
+ else
+#endif
+ {
+ rc = RTAsn1Core_CloneContent(&pThis->Asn1Core, &pSrc->Asn1Core, pAllocator);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ }
+ }
+
+ /* failed, clean up. */
+ if (pThis->Allocation.cbAllocated)
+ RTAsn1MemFree(&pThis->Allocation, (uint32_t *)pThis->pauComponents);
+ RT_ZERO(*pThis);
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(void) RTAsn1ObjId_Delete(PRTASN1OBJID pThis)
+{
+ if ( pThis
+ && RTAsn1ObjId_IsPresent(pThis))
+ {
+ Assert(pThis->Asn1Core.pOps == &g_RTAsn1ObjId_Vtable);
+
+ if (pThis->Allocation.cbAllocated)
+ RTAsn1MemFree(&pThis->Allocation, (uint32_t *)pThis->pauComponents);
+ RTAsn1ContentFree(&pThis->Asn1Core);
+ RT_ZERO(*pThis);
+ }
+}
+
+
+RTDECL(int) RTAsn1ObjId_Enum(PRTASN1OBJID pThis, PFNRTASN1ENUMCALLBACK pfnCallback, uint32_t uDepth, void *pvUser)
+{
+ RT_NOREF_PV(pThis); RT_NOREF_PV(pfnCallback); RT_NOREF_PV(uDepth); RT_NOREF_PV(pvUser);
+ Assert(pThis && (!RTAsn1ObjId_IsPresent(pThis) || pThis->Asn1Core.pOps == &g_RTAsn1ObjId_Vtable));
+
+ /* No children to enumerate. */
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1ObjId_Compare(PCRTASN1OBJID pLeft, PCRTASN1OBJID pRight)
+{
+ if (RTAsn1ObjId_IsPresent(pLeft))
+ {
+ if (RTAsn1ObjId_IsPresent(pRight))
+ {
+ uint8_t cComponents = RT_MIN(pLeft->cComponents, pRight->cComponents);
+ for (uint32_t i = 0; i < cComponents; i++)
+ if (pLeft->pauComponents[i] != pRight->pauComponents[i])
+ return pLeft->pauComponents[i] < pRight->pauComponents[i] ? -1 : 1;
+
+ if (pLeft->cComponents == pRight->cComponents)
+ return 0;
+ return pLeft->cComponents < pRight->cComponents ? -1 : 1;
+ }
+ return 1;
+ }
+ return 0 - (int)RTAsn1ObjId_IsPresent(pRight);
+}
+
+
+RTDECL(int) RTAsn1ObjId_CheckSanity(PCRTASN1OBJID pThis, uint32_t fFlags, PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ RT_NOREF_PV(fFlags);
+ if (RT_UNLIKELY(!RTAsn1ObjId_IsPresent(pThis)))
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_NOT_PRESENT, "%s: Missing (OBJID).", pszErrorTag);
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * Generate code for the associated collection types.
+ */
+#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-objid-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+#include <iprt/asn1-generator-core.h>
+#include <iprt/asn1-generator-init.h>
+#include <iprt/asn1-generator-sanity.h>
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-octetstring-decode.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-octetstring-decode.cpp
new file mode 100644
index 00000000..fbe8aaf5
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-octetstring-decode.cpp
@@ -0,0 +1,90 @@
+/* $Id: asn1-ut-octetstring-decode.cpp $ */
+/** @file
+ * IPRT - ASN.1, OCTET STRING Type, Decoding.
+ */
+
+/*
+ * 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/err.h>
+#include <iprt/string.h>
+
+#include <iprt/formats/asn1.h>
+
+
+RTDECL(int) RTAsn1OctetString_DecodeAsn1(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1OCTETSTRING pThis,
+ const char *pszErrorTag)
+{
+ pThis->pEncapsulated = NULL;
+ RTAsn1CursorInitAllocation(pCursor, &pThis->EncapsulatedAllocation);
+
+ int rc = RTAsn1CursorReadHdr(pCursor, &pThis->Asn1Core, pszErrorTag);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTAsn1CursorMatchTagClassFlagsString(pCursor, &pThis->Asn1Core, ASN1_TAG_OCTET_STRING,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ fFlags, pszErrorTag, "OCTET STRING");
+ if (RT_SUCCESS(rc))
+ {
+ if ( !(pThis->Asn1Core.fClass & ASN1_TAGFLAG_CONSTRUCTED)
+ || (fFlags & RTASN1CURSOR_GET_F_IMPLICIT) ) /* PKCS #7 ContentInfo tweak. */
+ {
+ RTAsn1CursorSkip(pCursor, pThis->Asn1Core.cb);
+ pThis->Asn1Core.pOps = &g_RTAsn1OctetString_Vtable;
+ pThis->Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
+ return VINF_SUCCESS;
+ }
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CONSTRUCTED_STRING_NOT_IMPL,
+ "%s: Constructed OCTET STRING not implemented.", pszErrorTag);
+ }
+ else
+ rc = RTAsn1CursorSetInfo(pCursor, rc, "%s: Not OCTET STRING: fClass=%#x / uTag=%#x",
+ pszErrorTag, pThis->Asn1Core.fClass, pThis->Asn1Core.uTag);
+ }
+ RT_ZERO(*pThis);
+ return rc;
+}
+
+
+/*
+ * Generate code for the associated collection types.
+ */
+#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-octetstring-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+#include <iprt/asn1-generator-asn1-decoder.h>
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-octetstring-template.h b/src/VBox/Runtime/common/asn1/asn1-ut-octetstring-template.h
new file mode 100644
index 00000000..b3668922
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-octetstring-template.h
@@ -0,0 +1,55 @@
+/* $Id: asn1-ut-octetstring-template.h $ */
+/** @file
+ * IPRT - ASN.1, Octet String Type, Collection Type Template.
+ */
+
+/*
+ * 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
+ */
+
+#define RTASN1TMPL_DECL RTDECL
+
+#define RTASN1TMPL_TYPE RTASN1SEQOFOCTETSTRINGS
+#define RTASN1TMPL_EXT_NAME RTAsn1SeqOfOctetStrings
+#define RTASN1TMPL_INT_NAME rtAsn1SeqOfOctetStrings
+RTASN1TMPL_SEQ_OF(RTASN1OCTETSTRING, RTAsn1OctetString);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+#define RTASN1TMPL_TYPE RTASN1SETOFOCTETSTRINGS
+#define RTASN1TMPL_EXT_NAME RTAsn1SetOfOctetStrings
+#define RTASN1TMPL_INT_NAME rtAsn1SetOfOctetStrings
+RTASN1TMPL_SET_OF(RTASN1OCTETSTRING, RTAsn1OctetString);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-octetstring.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-octetstring.cpp
new file mode 100644
index 00000000..32d6f40e
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-octetstring.cpp
@@ -0,0 +1,459 @@
+/* $Id: asn1-ut-octetstring.cpp $ */
+/** @file
+ * IPRT - ASN.1, Octet String.
+ *
+ * @remarks This file should remain very similar to asn1-ut-bitstring.cpp.
+ */
+
+/*
+ * 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 *
+*********************************************************************************************************************************/
+typedef struct RTASN1OCTETSTRINGWRITERCTX
+{
+ /** Pointer to the output buffer. */
+ uint8_t *pbBuf;
+ /** The current buffer offset. */
+ uint32_t offBuf;
+ /** The size of the buffer. */
+ uint32_t cbBuf;
+} RTASN1OCTETSTRINGWRITERCTX;
+
+
+/** @callback_method_impl{FNRTASN1ENCODEWRITER,
+ * Used to refresh the content of octet and bit strings. } */
+static DECLCALLBACK(int) rtAsn1OctetStringEncodeWriter(const void *pvBuf, size_t cbToWrite, void *pvUser, PRTERRINFO pErrInfo)
+{
+ RTASN1OCTETSTRINGWRITERCTX *pCtx = (RTASN1OCTETSTRINGWRITERCTX *)pvUser;
+ AssertReturn(cbToWrite <= pCtx->cbBuf - pCtx->offBuf,
+ RTErrInfoSetF(pErrInfo, VERR_BUFFER_OVERFLOW,
+ "cbToWrite=%#x offBuf=%#x cbBuf=%#x", cbToWrite, pCtx->cbBuf, pCtx->offBuf));
+ memcpy(&pCtx->pbBuf[pCtx->offBuf], pvBuf, cbToWrite);
+ pCtx->offBuf += (uint32_t)cbToWrite;
+ return VINF_SUCCESS;
+}
+
+
+/** @callback_method_impl{FNRTASN1ENCODEWRITER,
+ * Used to compare the encoded raw content of an octet or bit string with the
+ * encapsulated object. } */
+static DECLCALLBACK(int) rtAsn1OctetStringEncodeCompare(const void *pvBuf, size_t cbToWrite, void *pvUser, PRTERRINFO pErrInfo)
+{
+ RTASN1OCTETSTRINGWRITERCTX *pCtx = (RTASN1OCTETSTRINGWRITERCTX *)pvUser;
+ RT_NOREF_PV(pErrInfo);
+ AssertReturn(cbToWrite <= pCtx->cbBuf - pCtx->offBuf, VERR_BUFFER_OVERFLOW);
+ if (memcmp(&pCtx->pbBuf[pCtx->offBuf], pvBuf, cbToWrite) != 0)
+ return VERR_NOT_EQUAL;
+ pCtx->offBuf += (uint32_t)cbToWrite;
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * ASN.1 OCTET STRING - Specific Methods
+ */
+
+RTDECL(int) RTAsn1OctetString_RefreshContent(PRTASN1OCTETSTRING pThis, uint32_t fFlags,
+ PCRTASN1ALLOCATORVTABLE pAllocator, PRTERRINFO pErrInfo)
+{
+ AssertReturn(pThis->pEncapsulated, VERR_INVALID_STATE);
+
+ uint32_t cbEncoded;
+ int rc = RTAsn1EncodePrepare(pThis->pEncapsulated, fFlags, &cbEncoded, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->Asn1Core.cb = cbEncoded;
+
+ rc = RTAsn1ContentReallocZ(&pThis->Asn1Core, cbEncoded, pAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ /* Initialize the writer context. */
+ RTASN1OCTETSTRINGWRITERCTX Ctx;
+ Ctx.pbBuf = (uint8_t *)pThis->Asn1Core.uData.pu8;
+ Ctx.cbBuf = cbEncoded;
+ Ctx.offBuf = 0;
+
+ rc = RTAsn1EncodeWrite(pThis->pEncapsulated, fFlags, rtAsn1OctetStringEncodeWriter, &Ctx, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ if (Ctx.offBuf == cbEncoded)
+ return VINF_SUCCESS;
+
+ rc = RTErrInfoSetF(pErrInfo, rc, "Expected %#x bytes, got %#x", cbEncoded, Ctx.offBuf);
+ }
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "Error allocating %#x bytes for storing content\n", cbEncoded);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1OctetString_AllocContent(PRTASN1OCTETSTRING pThis, void const *pvSrc, size_t cb,
+ PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ AssertReturn(!pThis->pEncapsulated, VERR_INVALID_STATE);
+ int rc;
+ if (pvSrc)
+ rc = RTAsn1ContentDup(&pThis->Asn1Core, pvSrc, cb, pAllocator);
+ else
+ rc = RTAsn1ContentAllocZ(&pThis->Asn1Core, cb, pAllocator);
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1OctetString_SetContent(PRTASN1OCTETSTRING pThis, void const *pvSrc, size_t cbSrc,
+ PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ AssertPtrReturn(pvSrc, VERR_INVALID_POINTER);
+ return RTAsn1OctetString_AllocContent(pThis, pvSrc, cbSrc, pAllocator);
+}
+
+
+RTDECL(bool) RTAsn1OctetString_AreContentBytesValid(PCRTASN1OCTETSTRING pThis, uint32_t fFlags)
+{
+ if (pThis->pEncapsulated)
+ {
+ /* Check the encoded length of the octets. */
+ uint32_t cbEncoded;
+ int rc = RTAsn1EncodePrepare(pThis->pEncapsulated, fFlags, &cbEncoded, NULL);
+ if (RT_FAILURE(rc))
+ return false;
+ if (pThis->Asn1Core.cb != cbEncoded)
+ return false;
+
+ /* Check the encoded bytes, if there are any. */
+ if (cbEncoded)
+ {
+ if (!pThis->Asn1Core.uData.pv)
+ return false;
+
+ /* Check the other bytes. */
+ RTASN1OCTETSTRINGWRITERCTX Ctx;
+ Ctx.pbBuf = (uint8_t *)pThis->Asn1Core.uData.pu8;
+ Ctx.cbBuf = cbEncoded;
+ Ctx.offBuf = 0;
+ rc = RTAsn1EncodeWrite(pThis->pEncapsulated, fFlags, rtAsn1OctetStringEncodeCompare, &Ctx, NULL);
+ if (RT_FAILURE(rc))
+ return false;
+ }
+ }
+ return true;
+}
+
+
+/*
+ * ASN.1 OCTET STRING - Standard Methods.
+ */
+
+/** @interface_method_impl{FNRTASN1COREVTENCODEPREP} */
+static DECLCALLBACK(int) RTAsn1OctetString_EncodePrep(PRTASN1CORE pThisCore, uint32_t fFlags, PRTERRINFO pErrInfo)
+{
+ PRTASN1OCTETSTRING pThis = (PRTASN1OCTETSTRING)pThisCore;
+ if (!pThis->pEncapsulated)
+ {
+ Assert(pThis->Asn1Core.cb == 0 || pThis->Asn1Core.uData.pv);
+ return VINF_SUCCESS;
+ }
+
+ /* Figure out the size of the encapsulated content. */
+ uint32_t cbEncoded;
+ int rc = RTAsn1EncodePrepare(pThis->pEncapsulated, fFlags, &cbEncoded, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /* Free the bytes if they don't match up. */
+ if (pThis->Asn1Core.uData.pv)
+ {
+ bool fMustFree = pThis->Asn1Core.cb != cbEncoded;
+ if (!fMustFree)
+ {
+ RTASN1OCTETSTRINGWRITERCTX Ctx;
+ Ctx.pbBuf = (uint8_t *)pThis->Asn1Core.uData.pu8;
+ Ctx.cbBuf = cbEncoded;
+ Ctx.offBuf = 0;
+ rc = RTAsn1EncodeWrite(pThis->pEncapsulated, fFlags, rtAsn1OctetStringEncodeCompare, &Ctx, NULL);
+ fMustFree = RT_FAILURE_NP(rc);
+ }
+ if (fMustFree)
+ RTAsn1ContentFree(&pThis->Asn1Core);
+ }
+
+ pThis->Asn1Core.cb = cbEncoded;
+ rc = RTAsn1EncodeRecalcHdrSize(&pThis->Asn1Core, fFlags, pErrInfo);
+ }
+ return rc;
+}
+
+
+/** @interface_method_impl{FNRTASN1COREVTENCODEWRITE} */
+static DECLCALLBACK(int) RTAsn1OctetString_EncodeWrite(PRTASN1CORE pThisCore, uint32_t fFlags, PFNRTASN1ENCODEWRITER pfnWriter,
+ void *pvUser, PRTERRINFO pErrInfo)
+{
+ PRTASN1OCTETSTRING pThis = (PRTASN1OCTETSTRING)pThisCore;
+
+ /*
+ * First the header.
+ */
+ int rc = RTAsn1EncodeWriteHeader(&pThis->Asn1Core, fFlags, pfnWriter, pvUser, pErrInfo);
+ if (RT_SUCCESS(rc) && rc != VINF_ASN1_NOT_ENCODED)
+ {
+ /*
+ * If nothing is encapsulated, the core points to the content (if we have any).
+ */
+ if (!pThis->pEncapsulated)
+ {
+ if (pThis->Asn1Core.cb > 0)
+ rc = pfnWriter(pThis->Asn1Core.uData.pu8, pThis->Asn1Core.cb, pvUser, pErrInfo);
+ }
+ /*
+ * Call upon the encapsulated content to serialize itself.
+ */
+ else
+ rc = RTAsn1EncodeWrite(pThis->pEncapsulated, fFlags, pfnWriter, pvUser, pErrInfo);
+ }
+ return rc;
+}
+
+
+RT_DECL_DATA_CONST(RTASN1COREVTABLE const) g_RTAsn1OctetString_Vtable =
+{
+ "OctetString",
+ sizeof(RTASN1OCTETSTRING),
+ ASN1_TAG_OCTET_STRING,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ 0,
+ (PFNRTASN1COREVTDTOR)RTAsn1OctetString_Delete,
+ (PFNRTASN1COREVTENUM)RTAsn1OctetString_Enum,
+ (PFNRTASN1COREVTCLONE)RTAsn1OctetString_Clone,
+ (PFNRTASN1COREVTCOMPARE)RTAsn1OctetString_Compare,
+ (PFNRTASN1COREVTCHECKSANITY)RTAsn1OctetString_CheckSanity,
+ RTAsn1OctetString_EncodePrep,
+ RTAsn1OctetString_EncodeWrite
+};
+
+
+RTDECL(int) RTAsn1OctetString_Init(PRTASN1OCTETSTRING pThis, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ RT_ZERO(*pThis);
+
+ RTAsn1Core_InitEx(&pThis->Asn1Core, ASN1_TAG_OCTET_STRING, ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ &g_RTAsn1OctetString_Vtable, RTASN1CORE_F_PRESENT | RTASN1CORE_F_PRIMITE_TAG_STRUCT);
+ /*pThis->pEncapsulated = NULL;*/
+ RTAsn1MemInitAllocation(&pThis->EncapsulatedAllocation, pAllocator);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1OctetString_Clone(PRTASN1OCTETSTRING pThis, PCRTASN1OCTETSTRING pSrc, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ AssertPtr(pSrc); AssertPtr(pThis); AssertPtr(pAllocator);
+
+ RT_ZERO(*pThis);
+ if (RTAsn1OctetString_IsPresent(pSrc))
+ {
+ AssertReturn(pSrc->Asn1Core.pOps == &g_RTAsn1OctetString_Vtable, VERR_INTERNAL_ERROR_3);
+
+ int rc;
+ if (!pSrc->pEncapsulated)
+ rc = RTAsn1Core_CloneContent(&pThis->Asn1Core, &pSrc->Asn1Core, pAllocator);
+ else
+ rc = RTAsn1Core_CloneNoContent(&pThis->Asn1Core, &pSrc->Asn1Core);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ RTAsn1MemInitAllocation(&pThis->EncapsulatedAllocation, pAllocator);
+ if (pSrc->pEncapsulated)
+ {
+ PCRTASN1COREVTABLE pOps = pSrc->pEncapsulated->pOps;
+ Assert(!pOps || pOps->pfnClone);
+ if (pOps && pOps->pfnClone)
+ {
+ /* We can clone the decoded encapsulated object. */
+ rc = RTAsn1MemAllocZ(&pThis->EncapsulatedAllocation, (void **)&pThis->pEncapsulated, pOps->cbStruct);
+ if (RT_SUCCESS(rc))
+ {
+ rc = pOps->pfnClone(pThis->pEncapsulated, pSrc->pEncapsulated, pAllocator);
+ if (RT_FAILURE(rc))
+ RTAsn1MemFree(&pThis->EncapsulatedAllocation, pThis->pEncapsulated);
+ }
+ }
+ else
+ {
+ /* Borrow the encapsulated pointer and use RTAsn1OctetString_RefreshContent
+ to get an accurate copy of the bytes. */
+ pThis->pEncapsulated = pSrc->pEncapsulated;
+ rc = RTAsn1OctetString_RefreshContent(pThis, RTASN1ENCODE_F_DER, pAllocator, NULL);
+ pThis->pEncapsulated = NULL;
+ }
+ if (RT_FAILURE(rc))
+ {
+ RTAsn1ContentFree(&pThis->Asn1Core);
+ RT_ZERO(*pThis);
+ return rc;
+ }
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(void) RTAsn1OctetString_Delete(PRTASN1OCTETSTRING pThis)
+{
+ if ( pThis
+ && RTAsn1OctetString_IsPresent(pThis))
+ {
+ Assert(pThis->Asn1Core.pOps == &g_RTAsn1OctetString_Vtable);
+
+ /* Destroy the encapsulated object. */
+ if (pThis->pEncapsulated)
+ {
+ RTAsn1VtDelete(pThis->pEncapsulated);
+ if (pThis->EncapsulatedAllocation.cbAllocated)
+ RTAsn1MemFree(&pThis->EncapsulatedAllocation, pThis->pEncapsulated);
+ }
+
+ /* Delete content and wipe the content. */
+ RTAsn1ContentFree(&pThis->Asn1Core);
+ RT_ZERO(*pThis);
+ }
+}
+
+
+RTDECL(int) RTAsn1OctetString_Enum(PRTASN1OCTETSTRING pThis, PFNRTASN1ENUMCALLBACK pfnCallback, uint32_t uDepth, void *pvUser)
+{
+ Assert(pThis && (!RTAsn1OctetString_IsPresent(pThis) || pThis->Asn1Core.pOps == &g_RTAsn1OctetString_Vtable));
+
+ /* Enumerate the encapsulated object if present. */
+ if (pThis->pEncapsulated)
+ return pfnCallback(pThis->pEncapsulated, "Encapsulated", uDepth + 1, pvUser);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1OctetString_Compare(PCRTASN1OCTETSTRING pLeft, PCRTASN1OCTETSTRING pRight)
+{
+ Assert(pLeft && (!RTAsn1OctetString_IsPresent(pLeft) || pLeft->Asn1Core.pOps == &g_RTAsn1OctetString_Vtable));
+ Assert(pRight && (!RTAsn1OctetString_IsPresent(pRight) || pRight->Asn1Core.pOps == &g_RTAsn1OctetString_Vtable));
+
+ int iDiff;
+ if (RTAsn1OctetString_IsPresent(pLeft))
+ {
+ if (RTAsn1OctetString_IsPresent(pRight))
+ {
+ /* Since it's really hard to tell whether encapsulated objects have
+ been modified or not, we might have to refresh both objects
+ while doing this compare. We'll try our best to avoid it though. */
+ if (pLeft->pEncapsulated || pRight->pEncapsulated)
+ {
+ if ( pLeft->pEncapsulated
+ && pRight->pEncapsulated
+ && pLeft->pEncapsulated->pOps == pRight->pEncapsulated->pOps)
+ iDiff = pLeft->pEncapsulated->pOps->pfnCompare(pLeft->pEncapsulated, pRight->pEncapsulated);
+ else
+ {
+ /* No direct comparison of encapsulated objects possible,
+ make sure we've got the rigth bytes then. */
+ if ( pLeft->pEncapsulated
+ && !RTAsn1OctetString_AreContentBytesValid(pLeft, RTASN1ENCODE_F_DER))
+ {
+ int rc = RTAsn1OctetString_RefreshContent((PRTASN1OCTETSTRING)pLeft, RTASN1ENCODE_F_DER,
+ pLeft->EncapsulatedAllocation.pAllocator, NULL);
+ AssertRC(rc);
+ }
+
+ if ( pRight->pEncapsulated
+ && !RTAsn1OctetString_AreContentBytesValid(pRight, RTASN1ENCODE_F_DER))
+ {
+ int rc = RTAsn1OctetString_RefreshContent((PRTASN1OCTETSTRING)pRight, RTASN1ENCODE_F_DER,
+ pRight->EncapsulatedAllocation.pAllocator, NULL);
+ AssertRC(rc);
+ }
+
+ /* Compare the content bytes. */
+ iDiff = RTAsn1Core_CompareEx(&pLeft->Asn1Core, &pRight->Asn1Core, true /*fIgnoreTagAndClass*/);
+ }
+ }
+ /*
+ * No encapsulated object, just compare the raw content bytes.
+ */
+ else
+ iDiff = RTAsn1Core_CompareEx(&pLeft->Asn1Core, &pRight->Asn1Core, true /*fIgnoreTagAndClass*/);
+ }
+ else
+ iDiff = -1;
+ }
+ else
+ iDiff = 0 - (int)RTAsn1OctetString_IsPresent(pRight);
+ return iDiff;
+}
+
+
+RTDECL(int) RTAsn1OctetString_CheckSanity(PCRTASN1OCTETSTRING pThis, uint32_t fFlags, PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ if (RT_UNLIKELY(!RTAsn1OctetString_IsPresent(pThis)))
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_NOT_PRESENT, "%s: Missing (OCTET STRING).", pszErrorTag);
+
+ if (pThis->pEncapsulated)
+ return pThis->pEncapsulated->pOps->pfnCheckSanity(pThis->pEncapsulated, fFlags & RTASN1_CHECK_SANITY_F_COMMON_MASK,
+ pErrInfo, pszErrorTag);
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * Generate code for the associated collection types.
+ */
+#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-octetstring-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+#include <iprt/asn1-generator-core.h>
+#include <iprt/asn1-generator-init.h>
+#include <iprt/asn1-generator-sanity.h>
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-string-decode.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-string-decode.cpp
new file mode 100644
index 00000000..d9444d1c
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-string-decode.cpp
@@ -0,0 +1,199 @@
+/* $Id: asn1-ut-string-decode.cpp $ */
+/** @file
+ * IPRT - ASN.1, XXX STRING Types, Decoding.
+ */
+
+/*
+ * 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/err.h>
+#include <iprt/string.h>
+#include <iprt/ctype.h>
+
+#include <iprt/formats/asn1.h>
+
+
+RTDECL(int) RTAsn1String_DecodeAsn1(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1STRING pThis, const char *pszErrorTag)
+{
+ RT_ZERO(*pThis);
+ AssertReturn(!(fFlags & RTASN1CURSOR_GET_F_IMPLICIT), VERR_INVALID_PARAMETER);
+
+ int rc = RTAsn1CursorReadHdr(pCursor, &pThis->Asn1Core, pszErrorTag);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Do tag matching.
+ */
+ switch (pThis->Asn1Core.uTag)
+ {
+ case ASN1_TAG_UTF8_STRING:
+ case ASN1_TAG_NUMERIC_STRING:
+ case ASN1_TAG_PRINTABLE_STRING:
+ case ASN1_TAG_T61_STRING:
+ case ASN1_TAG_VIDEOTEX_STRING:
+ case ASN1_TAG_IA5_STRING:
+ case ASN1_TAG_GENERALIZED_TIME:
+ case ASN1_TAG_GRAPHIC_STRING:
+ case ASN1_TAG_VISIBLE_STRING:
+ case ASN1_TAG_GENERAL_STRING:
+ case ASN1_TAG_UNIVERSAL_STRING:
+ case ASN1_TAG_BMP_STRING:
+ rc = VINF_SUCCESS;
+ break;
+ default:
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_TAG_MISMATCH,
+ "%s: Not a string object: fClass=%#x / uTag=%#x",
+ pszErrorTag, pThis->Asn1Core.fClass, pThis->Asn1Core.uTag);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Match flags. CER/DER makes it complicated.
+ */
+ if (pThis->Asn1Core.fClass == (ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE))
+ {
+ /*
+ * Primitive strings are simple.
+ */
+ RTAsn1CursorSkip(pCursor, pThis->Asn1Core.cb);
+ pThis->Asn1Core.pOps = &g_RTAsn1String_Vtable;
+ pThis->Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
+ RTAsn1CursorInitAllocation(pCursor, &pThis->Allocation);
+
+ /* UTF-8 conversion is done lazily, upon request. */
+ return VINF_SUCCESS;
+ }
+
+ if (pThis->Asn1Core.fClass == (ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_CONSTRUCTED))
+ {
+ /*
+ * Constructed strings are not yet fully implemented.
+ */
+ if (pCursor->fFlags & RTASN1CURSOR_FLAGS_DER)
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_ILLEGAL_CONSTRUCTED_STRING,
+ "%s: DER encoding does not allow constructed strings (cb=%#x uTag=%#x fClass=%#x)",
+ pszErrorTag, pThis->Asn1Core.cb, pThis->Asn1Core.uTag, pThis->Asn1Core.fClass);
+ else if (pCursor->fFlags & RTASN1CURSOR_FLAGS_CER)
+ {
+ if (pThis->Asn1Core.cb > 1000)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_ILLEGAL_CONSTRUCTED_STRING,
+ "%s: Constructed strings only allowed for >1000 byte in CER encoding: cb=%#x uTag=%#x fClass=%#x",
+ pszErrorTag, pThis->Asn1Core.cb,
+ pThis->Asn1Core.uTag, pThis->Asn1Core.fClass);
+ }
+ /** @todo implement constructed strings. */
+ if (RT_SUCCESS(rc))
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CONSTRUCTED_STRING_NOT_IMPL,
+ "%s: Support for constructed strings is not implemented", pszErrorTag);
+ }
+ else
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_TAG_FLAG_CLASS_MISMATCH,
+ "%s: Not a valid string object: fClass=%#x / uTag=%#x",
+ pszErrorTag, pThis->Asn1Core.fClass, pThis->Asn1Core.uTag);
+ }
+ }
+ RT_ZERO(*pThis);
+ return rc;
+}
+
+
+/**
+ * Common worker for the specific string type getters.
+ *
+ * @returns IPRT status code
+ * @param pCursor The cursor.
+ * @param fFlags The RTASN1CURSOR_GET_F_XXX flags.
+ * @param uTag The string tag.
+ * @param pThis The output object.
+ * @param pszErrorTag The error tag.
+ * @param pszWhat The string type name.
+ */
+static int rtAsn1XxxString_DecodeAsn1(PRTASN1CURSOR pCursor, uint32_t fFlags, uint8_t uTag, PRTASN1STRING pThis,
+ const char *pszErrorTag, const char *pszWhat)
+{
+ pThis->cchUtf8 = 0;
+ pThis->pszUtf8 = NULL;
+
+ int rc = RTAsn1CursorReadHdr(pCursor, &pThis->Asn1Core, pszErrorTag);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTAsn1CursorMatchTagClassFlagsString(pCursor, &pThis->Asn1Core, uTag,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ fFlags, pszErrorTag, pszWhat);
+ if (RT_SUCCESS(rc))
+ {
+ if (!(pThis->Asn1Core.fClass & ASN1_TAGFLAG_CONSTRUCTED))
+ {
+ RTAsn1CursorSkip(pCursor, pThis->Asn1Core.cb);
+ pThis->Asn1Core.pOps = &g_RTAsn1String_Vtable;
+ pThis->Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
+ RTAsn1CursorInitAllocation(pCursor, &pThis->Allocation);
+ /* UTF-8 conversion is done lazily, upon request. */
+ return VINF_SUCCESS;
+ }
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CONSTRUCTED_STRING_NOT_IMPL,
+ "%s: Constructed %s not implemented.", pszErrorTag, pszWhat);
+ }
+ }
+ RT_ZERO(*pThis);
+ return rc;
+}
+
+
+/*
+ * Generate code for the tag specific decoders.
+ */
+#define RTASN1STRING_IMPL(a_uTag, a_szTag, a_Api) \
+ RTDECL(int) RT_CONCAT(a_Api,_DecodeAsn1)(PRTASN1CURSOR pCursor, uint32_t fFlags, \
+ PRTASN1STRING pThis, const char *pszErrorTag) \
+ { \
+ return rtAsn1XxxString_DecodeAsn1(pCursor, fFlags, a_uTag, pThis, pszErrorTag, a_szTag); \
+ }
+#include "asn1-ut-string-template2.h"
+
+
+/*
+ * Generate code for the associated collection types.
+ */
+#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-string-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+#include <iprt/asn1-generator-asn1-decoder.h>
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-string-template.h b/src/VBox/Runtime/common/asn1/asn1-ut-string-template.h
new file mode 100644
index 00000000..cb1f98b8
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-string-template.h
@@ -0,0 +1,55 @@
+/* $Id: asn1-ut-string-template.h $ */
+/** @file
+ * IPRT - ASN.1, XXX STRING Types, Collection Type Template.
+ */
+
+/*
+ * 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
+ */
+
+#define RTASN1TMPL_DECL RTDECL
+
+#define RTASN1TMPL_TYPE RTASN1SEQOFSTRINGS
+#define RTASN1TMPL_EXT_NAME RTAsn1SeqOfStrings
+#define RTASN1TMPL_INT_NAME rtAsn1SeqOfStrings
+RTASN1TMPL_SEQ_OF(RTASN1STRING, RTAsn1String);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+#define RTASN1TMPL_TYPE RTASN1SETOFSTRINGS
+#define RTASN1TMPL_EXT_NAME RTAsn1SetOfStrings
+#define RTASN1TMPL_INT_NAME rtAsn1SetOfStrings
+RTASN1TMPL_SET_OF(RTASN1STRING, RTAsn1String);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-string-template2.h b/src/VBox/Runtime/common/asn1/asn1-ut-string-template2.h
new file mode 100644
index 00000000..217fe3d8
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-string-template2.h
@@ -0,0 +1,49 @@
+/* $Id: asn1-ut-string-template2.h $ */
+/** @file
+ * IPRT - ASN.1, XXX STRING Types, Template for type specific wrappers.
+ */
+
+/*
+ * 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
+ */
+
+
+RTASN1STRING_IMPL(ASN1_TAG_NUMERIC_STRING, "NUMERIC STRING", RTAsn1NumericString)
+RTASN1STRING_IMPL(ASN1_TAG_PRINTABLE_STRING, "PRINTABLE STRING", RTAsn1PrintableString)
+RTASN1STRING_IMPL(ASN1_TAG_T61_STRING, "T61 STRING", RTAsn1T61String)
+RTASN1STRING_IMPL(ASN1_TAG_VIDEOTEX_STRING, "VIDEOTEX STRING", RTAsn1VideotexString)
+RTASN1STRING_IMPL(ASN1_TAG_VISIBLE_STRING, "VISIBLE STRING", RTAsn1VisibleString)
+RTASN1STRING_IMPL(ASN1_TAG_IA5_STRING, "IA5 STRING", RTAsn1Ia5String)
+RTASN1STRING_IMPL(ASN1_TAG_GRAPHIC_STRING, "GRAPHIC STRING", RTAsn1GraphicString)
+RTASN1STRING_IMPL(ASN1_TAG_GENERAL_STRING, "GENERAL STRING", RTAsn1GeneralString)
+RTASN1STRING_IMPL(ASN1_TAG_UTF8_STRING, "UTF8 STRING", RTAsn1Utf8String)
+RTASN1STRING_IMPL(ASN1_TAG_BMP_STRING, "BMP STRING", RTAsn1BmpString)
+RTASN1STRING_IMPL(ASN1_TAG_UNIVERSAL_STRING, "UNIVERSAL STRING", RTAsn1UniversalString)
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-string.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-string.cpp
new file mode 100644
index 00000000..edec7957
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-string.cpp
@@ -0,0 +1,1855 @@
+/* $Id: asn1-ut-string.cpp $ */
+/** @file
+ * IPRT - ASN.1, XXX STRING Types.
+ */
+
+/*
+ * 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/ctype.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/uni.h>
+
+#include <iprt/formats/asn1.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static uint8_t const g_acbStringTags[] =
+{
+ /* [ASN1_TAG_EOC] = */ 0,
+ /* [ASN1_TAG_BOOLEAN] = */ 0,
+ /* [ASN1_TAG_INTEGER] = */ 0,
+ /* [ASN1_TAG_BIT_STRING] = */ 0,
+ /* [ASN1_TAG_OCTET_STRING] = */ 0,
+ /* [ASN1_TAG_NULL] = */ 0,
+ /* [ASN1_TAG_OID] = */ 0,
+ /* [ASN1_TAG_OBJECT_DESCRIPTOR] = */ 0,
+ /* [ASN1_TAG_EXTERNAL] = */ 0,
+ /* [ASN1_TAG_REAL] = */ 0,
+ /* [ASN1_TAG_ENUMERATED] = */ 0,
+ /* [ASN1_TAG_EMBEDDED_PDV] = */ 0,
+ /* [ASN1_TAG_UTF8_STRING] = */ 1,
+ /* [ASN1_TAG_RELATIVE_OID] = */ 0,
+ /* [ASN1_TAG_RESERVED_14] = */ 0,
+ /* [ASN1_TAG_RESERVED_15] = */ 0,
+ /* [ASN1_TAG_SEQUENCE] = */ 0,
+ /* [ASN1_TAG_SET] = */ 0,
+ /* [ASN1_TAG_NUMERIC_STRING] = */ 1,
+ /* [ASN1_TAG_PRINTABLE_STRING] = */ 1,
+ /* [ASN1_TAG_T61_STRING] = */ 1,
+ /* [ASN1_TAG_VIDEOTEX_STRING] = */ 1,
+ /* [ASN1_TAG_IA5_STRING] = */ 1,
+ /* [ASN1_TAG_UTC_TIME] = */ 0,
+ /* [ASN1_TAG_GENERALIZED_TIME] = */ 0,
+ /* [ASN1_TAG_GRAPHIC_STRING] = */ 1,
+ /* [ASN1_TAG_VISIBLE_STRING] = */ 1,
+ /* [ASN1_TAG_GENERAL_STRING] = */ 1,
+ /* [ASN1_TAG_UNIVERSAL_STRING] = */ 4,
+ /* [ASN1_TAG_CHARACTER_STRING] = */ 1,
+ /* [ASN1_TAG_BMP_STRING] = */ 2,
+};
+
+
+
+
+/*
+ * ISO/IEC-2022 + TeletexString mess.
+ */
+
+/**
+ * ISO-2022 codepoint mappings.
+ */
+typedef struct RTISO2022MAP
+{
+ /** The number of bytes per character. */
+ uint8_t cb;
+ /** The registration number. */
+ uint16_t uRegistration;
+ /** The size of the pauToUni table. */
+ uint16_t cToUni;
+ /** Pointer to the convertion table from ISO-2022 to Unicode.
+ * ASSUMES that unicode chars above 0xffff won't be required. */
+ uint16_t const *pauToUni;
+
+ /** Escape sequence for loading into G0 or C0 or C1 depending on the type (sans
+ * ESC). */
+ uint8_t abEscLoadXX[6];
+ /** Escape sequence for loading into G1 (sans ESC). */
+ uint8_t abEscLoadG1[6];
+ /** Escape sequence for loading into G2 (sans ESC). */
+ uint8_t abEscLoadG2[6];
+ /** Escape sequence for loading into G3 (sans ESC). */
+ uint8_t abEscLoadG3[6];
+} RTISO2022MAP;
+/** Pointer to const ISO-2022 mappings. */
+typedef RTISO2022MAP const *PCRTISO2022MAP;
+
+/** Unused codepoint value. */
+#define RTISO2022_UNUSED UINT16_C(0xffff)
+
+
+/** Dummy mappings to avoid dealing with NULL pointers in the decoder
+ * registers. */
+static const RTISO2022MAP g_DummyMap =
+{
+ 1, UINT16_MAX, 0, NULL,
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* No escape into G0 */,
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* No escape into G1 */,
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* No escape into G2 */,
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* No escape into G3 */
+};
+
+
+/** GL mappings for ISO-IR-168 (Japanese, update of #87), with space and
+ * delete. */
+static const RTISO2022MAP g_IsoIr168Map =
+{
+ //2, 168, RT_ELEMENTS(g_awcIsoIr168Decode), g_awcIsoIr168Decode,
+ 2, 168, 0, NULL,
+ { 0x26, 0x40, 0x2b, 0x24, 0x42, 0xff } /* Esc into G0 */,
+ { 0x26, 0x40, 0x2b, 0x24, 0x29, 0x42 } /* Esc into G1 */,
+ { 0x26, 0x40, 0x2b, 0x24, 0x2a, 0x42 } /* Esc into G2 */,
+ { 0x26, 0x40, 0x2b, 0x24, 0x2b, 0x42 } /* Esc into G3 */,
+};
+
+
+/** GL mappings for ISO-IR-165 (Chinese), with space and delete. */
+static const RTISO2022MAP g_IsoIr165Map =
+{
+ //2, 165, RT_ELEMENTS(g_awcIsoIr165Decode), g_awcIsoIr165Decode,
+ 2, 165, 0, NULL,
+ { 0x24, 0x28, 0x45, 0xff, 0xff, 0xff } /* Esc into G0 */,
+ { 0x24, 0x29, 0x45, 0xff, 0xff, 0xff } /* Esc into G1 */,
+ { 0x24, 0x2a, 0x45, 0xff, 0xff, 0xff } /* Esc into G2 */,
+ { 0x24, 0x2b, 0x45, 0xff, 0xff, 0xff } /* Esc into G3 */,
+};
+
+
+/** GL mappings for ISO-IR-150 (Greek), with space and delete. */
+static const RTISO2022MAP g_IsoIr150Map =
+{
+ //1, 150, RT_ELEMENTS(g_awcIsoIr150Decode), g_awcIsoIr150Decode,
+ 1, 150, 0, NULL,
+ { 0x28, 0x21, 0x40, 0xff, 0xff, 0xff } /* Esc into G0 */,
+ { 0x29, 0x21, 0x40, 0xff, 0xff, 0xff } /* Esc into G1 */,
+ { 0x2a, 0x21, 0x40, 0xff, 0xff, 0xff } /* Esc into G2 */,
+ { 0x2b, 0x21, 0x40, 0xff, 0xff, 0xff } /* Esc into G3 */,
+};
+
+
+/** GL mappings for ISO-IR-103 (Teletex supplementary), with space and
+ * delete. */
+static const RTISO2022MAP g_IsoIr103Map =
+{
+ //1, 103, RT_ELEMENTS(g_awcIsoIr103Decode), g_awcIsoIr103Decode,
+ 1, 103, 0, NULL,
+ { 0x28, 0x76, 0xff, 0xff, 0xff, 0xff } /* Esc into G0 */,
+ { 0x29, 0x76, 0xff, 0xff, 0xff, 0xff } /* Esc into G1 */,
+ { 0x2a, 0x76, 0xff, 0xff, 0xff, 0xff } /* Esc into G2 */,
+ { 0x2b, 0x76, 0xff, 0xff, 0xff, 0xff } /* Esc into G3 */,
+};
+
+
+/**
+ * GL mapping from ISO-IR-102 (Teletex primary) to unicode, with space and
+ * delete.
+ *
+ * Mostly 1:1, except that (a) what would be dollar is currency sign, (b)
+ * positions 0x5c, 0x5e, 0x7b, 0x7d and 0x7e are defined not to be used.
+ */
+static uint16_t const g_awcIsoIr102Decode[0x60] =
+{
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x00A4, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0xffff, 0x005d, 0xffff, 0x005f,
+ 0xffff, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0xffff, 0x007c, 0xffff, 0xffff, 0x007f,
+};
+
+/** GL mappings for ISO-IR-102, with space and delete. */
+static const RTISO2022MAP g_IsoIr102Map =
+{
+ 1, 102, RT_ELEMENTS(g_awcIsoIr102Decode), g_awcIsoIr102Decode,
+ { 0x28, 0x75, 0xff, 0xff, 0xff, 0xff } /* Esc into G0 */,
+ { 0x29, 0x75, 0xff, 0xff, 0xff, 0xff } /* Esc into G1 */,
+ { 0x2a, 0x75, 0xff, 0xff, 0xff, 0xff } /* Esc into G2 */,
+ { 0x2b, 0x75, 0xff, 0xff, 0xff, 0xff } /* Esc into G3 */,
+};
+
+
+#if 0 /* unused */
+/** GL mappings for ISO-IR-87 (Japanese), with space and delete. */
+static const RTISO2022MAP g_IsoIr87Map =
+{
+ //1, 87, RT_ELEMENTS(g_awcIsoIr87Decode), g_awcIsoIr97Decode,
+ 1, 87, 0, NULL,
+ { 0x24, 0x42, 0xff, 0xff, 0xff, 0xff } /* Esc into G0 */,
+ { 0x24, 0x29, 0x42, 0xff, 0xff, 0xff } /* Esc into G1 */,
+ { 0x24, 0x2a, 0x42, 0xff, 0xff, 0xff } /* Esc into G2 */,
+ { 0x24, 0x2b, 0x42, 0xff, 0xff, 0xff } /* Esc into G3 */,
+};
+#endif
+
+
+/**
+ * GL mapping from ISO-IR-6 (ASCII) to unicode, with space and delete.
+ *
+ * Completely 1:1.
+ */
+static uint16_t const g_awcIsoIr6Decode[0x60] =
+{
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x006b, 0x007c, 0x007d, 0x007e, 0x007f,
+};
+
+/** GL mappings for ISO-IR-6 (ASCII), with space and delete. */
+static const RTISO2022MAP g_IsoIr6Map =
+{
+ 1, 6, RT_ELEMENTS(g_awcIsoIr6Decode), g_awcIsoIr6Decode,
+ { 0x28, 0x42, 0xff, 0xff, 0xff, 0xff } /* Esc into G0 */,
+ { 0x29, 0x42, 0xff, 0xff, 0xff, 0xff } /* Esc into G1 */,
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* Esc into G2 */,
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* Esc into G3 */,
+};
+
+
+/** GL maps. */
+static PCRTISO2022MAP g_paGLMaps[] =
+{
+ &g_IsoIr6Map,
+ &g_IsoIr102Map,
+ &g_IsoIr103Map,
+ &g_IsoIr150Map,
+ &g_IsoIr165Map,
+ &g_IsoIr168Map,
+};
+
+
+
+/** GR mappings for ISO-IR-164 (Hebrew supplementary). */
+static const RTISO2022MAP g_IsoIr164Map =
+{
+ //1, 164, RT_ELEMENTS(g_awcIsoIr164Decode), g_awcIsoIr164Decode
+ 1, 164, 0, NULL,
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* Esc into G0 */,
+ { 0x2d, 0x53, 0xff, 0xff, 0xff, 0xff } /* Esc into G1 */,
+ { 0x2e, 0x53, 0xff, 0xff, 0xff, 0xff } /* Esc into G2 */,
+ { 0x2f, 0x53, 0xff, 0xff, 0xff, 0xff } /* Esc into G3 */,
+};
+
+
+/** GR mappings for ISO-IR-156 (Supplementary for ASCII (#6)). */
+static const RTISO2022MAP g_IsoIr156Map =
+{
+ //1, 156, RT_ELEMENTS(g_awcIsoIr156Decode), g_awcIsoIr156Decode
+ 1, 156, 0, NULL,
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* Esc into G0 */,
+ { 0x2d, 0x52, 0xff, 0xff, 0xff, 0xff } /* Esc into G1 */,
+ { 0x2e, 0x52, 0xff, 0xff, 0xff, 0xff } /* Esc into G2 */,
+ { 0x2f, 0x52, 0xff, 0xff, 0xff, 0xff } /* Esc into G3 */,
+};
+
+
+/** GR mappings for ISO-IR-153 (Basic Cyrillic). */
+static const RTISO2022MAP g_IsoIr153Map =
+{
+ //1, 153, RT_ELEMENTS(g_awcIsoIr153Decode), g_awcIsoIr153Decode
+ 1, 153, 0, NULL,
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* Esc into G0 */,
+ { 0x2d, 0x4f, 0xff, 0xff, 0xff, 0xff } /* Esc into G1 */,
+ { 0x2e, 0x4f, 0xff, 0xff, 0xff, 0xff } /* Esc into G2 */,
+ { 0x2f, 0x4f, 0xff, 0xff, 0xff, 0xff } /* Esc into G3 */,
+};
+
+
+/** GR mappings for ISO-IR-144 (Cryllic part of Latin/Cyrillic). */
+static const RTISO2022MAP g_IsoIr144Map =
+{
+ //1, 144, RT_ELEMENTS(g_awcIsoIr144Decode), g_awcIsoIr144Decode
+ 1, 144, 0, NULL,
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* Esc into G0 */,
+ { 0x2d, 0x4f, 0xff, 0xff, 0xff, 0xff } /* Esc into G1 */,
+ { 0x2e, 0x4f, 0xff, 0xff, 0xff, 0xff } /* Esc into G2 */,
+ { 0x2f, 0x4f, 0xff, 0xff, 0xff, 0xff } /* Esc into G3 */,
+};
+
+
+/** GR mappings for ISO-IR-126 (Latin/Greek). */
+static const RTISO2022MAP g_IsoIr126Map =
+{
+ //1, 126, RT_ELEMENTS(g_awcIsoIr126Decode), g_awcIsoIr126Decode
+ 1, 126, 0, NULL,
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* Esc into G0 */,
+ { 0x2d, 0x46, 0xff, 0xff, 0xff, 0xff } /* Esc into G1 */,
+ { 0x2e, 0x46, 0xff, 0xff, 0xff, 0xff } /* Esc into G2 */,
+ { 0x2f, 0x46, 0xff, 0xff, 0xff, 0xff } /* Esc into G3 */,
+};
+
+
+/** GR maps. */
+static PCRTISO2022MAP g_paGRMaps[] =
+{
+ &g_IsoIr126Map,
+ &g_IsoIr144Map,
+ &g_IsoIr153Map,
+ &g_IsoIr156Map,
+ &g_IsoIr164Map,
+};
+
+
+
+/** C0 mapping from ISO-IR-106 to unicode. */
+static uint16_t g_awcIsoIr106Decode[0x20] =
+{
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x0008, 0xffff, 0x000a, 0xffff, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x008e, 0x000a, 0x001b, 0xffff, 0x008f, 0xffff, 0xffff,
+};
+
+/** C0 mappings for ISO-IR-106. */
+static const RTISO2022MAP g_IsoIr106Map =
+{
+ 1, 106, RT_ELEMENTS(g_awcIsoIr106Decode), g_awcIsoIr106Decode,
+ { 0x21, 0x45, 0xff, 0xff, 0xff, 0xff } /* Esc into C0 */,
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* N/A */,
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* N/A */,
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* N/A */,
+};
+
+/** C0 maps. */
+static PCRTISO2022MAP g_paC0Maps[] =
+{
+ &g_IsoIr106Map,
+};
+
+
+
+/** C1 mapping from ISO-IR-107 to unicode. */
+static uint16_t g_awcIsoIr107Decode[0x20] =
+{
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x008b, 0x008c, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x009b, 0xffff, 0xffff, 0xffff, 0xffff,
+};
+
+/** C1 mappings for ISO-IR-107. */
+static const RTISO2022MAP g_IsoIr107Map =
+{
+ 1, 107, RT_ELEMENTS(g_awcIsoIr107Decode), g_awcIsoIr107Decode,
+ { 0x22, 0x48, 0xff, 0xff, 0xff, 0xff } /* Esc into C1 */,
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* N/A */,
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* N/A */,
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /* N/A */,
+};
+
+/** C1 maps. */
+static PCRTISO2022MAP g_paC1Maps[] =
+{
+ &g_IsoIr107Map,
+};
+
+
+static int rtIso2022Decoder_LookupAndSet(PCRTISO2022MAP *ppMapRet, uint16_t uRegistration, PCRTISO2022MAP *papMaps, uint32_t cMaps)
+{
+ uint32_t i = cMaps;
+ while (i-- > 0)
+ if (papMaps[i]->uRegistration == uRegistration)
+ {
+ /** @todo skip non-Teletex codesets if we ever add more than we need for it. */
+ *ppMapRet = papMaps[i];
+ return VINF_SUCCESS;
+ }
+ return VERR_ASN1_INVALID_T61_STRING_ENCODING;
+}
+
+
+/**
+ * ISO-2022 decoder state.
+ */
+typedef struct RTISO2022DECODERSTATE
+{
+ /** Pointer to the string */
+ uint8_t const *pabString;
+ /** The string size. */
+ uint32_t cbString;
+ /** The current string position. */
+ uint32_t offString;
+
+ /** The GL mapping. */
+ PCRTISO2022MAP pMapGL;
+ /** The GR mapping. */
+ PCRTISO2022MAP pMapGR;
+ /** The lower control set (C0) mapping. */
+ PCRTISO2022MAP pMapC0;
+ /** The higher control set (C1) mapping. */
+ PCRTISO2022MAP pMapC1;
+ /** The G0, G1, G2, and G3 mappings. */
+ PCRTISO2022MAP apMapGn[4];
+ /** Used by SS2 & SS3 to store the orignal GL value that is to be restored. */
+ PCRTISO2022MAP pRestoreGL;
+ /** Pointer to extended error info buffer, optional. */
+ PRTERRINFO pErrInfo;
+} RTISO2022DECODERSTATE;
+/** Pointer to a const ISO-2022 decoder state. */
+typedef RTISO2022DECODERSTATE *PRTISO2022DECODERSTATE;
+
+
+static int rtIso2022Decoder_SetGL(PRTISO2022DECODERSTATE pThis, PCRTISO2022MAP pNewMap)
+{
+ pThis->pMapGL = pNewMap;
+ return VINF_SUCCESS;
+}
+
+
+static int rtIso2022Decoder_SetGR(PRTISO2022DECODERSTATE pThis, PCRTISO2022MAP pNewMap)
+{
+ pThis->pMapGR = pNewMap;
+ return VINF_SUCCESS;
+}
+
+
+static int rtIso2022Decoder_SetGLForOneChar(PRTISO2022DECODERSTATE pThis, PCRTISO2022MAP pTmpMap)
+{
+ pThis->pRestoreGL = pThis->pMapGL;
+ pThis->pMapGL = pTmpMap;
+ return VINF_SUCCESS;
+}
+
+
+static int rtIso2022Decoder_SetC0(PRTISO2022DECODERSTATE pThis, uint16_t uRegistration)
+{
+ return rtIso2022Decoder_LookupAndSet(&pThis->pMapC0, uRegistration, g_paC0Maps, RT_ELEMENTS(g_paC0Maps));
+}
+
+
+static int rtIso2022Decoder_SetC1(PRTISO2022DECODERSTATE pThis, uint16_t uRegistration)
+{
+ return rtIso2022Decoder_LookupAndSet(&pThis->pMapC1, uRegistration, g_paC1Maps, RT_ELEMENTS(g_paC1Maps));
+}
+
+
+/**
+ * Worker for rtIso2022Decoder_FindEscAndSet.
+ *
+ * @returns true if match, false if not.
+ * @param pabLeft Pointer to the first string byte after the ESC.
+ * @param cbLeft The number of bytes left in the string.
+ * @param pabRight Pointer to the abEscLoad* byte array to match with.
+ * @param cbRight Size of the mapping sequence (fixed).
+ * @param pcchMatch Where to return the length of the escape sequence (sans
+ * ESC) on success.
+ */
+static bool rtIso2022Decoder_MatchEscSeqFrom2ndByte(uint8_t const *pabLeft, uint32_t cbLeft,
+ uint8_t const *pabRight, uint32_t cbRight,
+ uint32_t *pcchMatch)
+{
+ Assert(cbRight == 6);
+ uint32_t i = 1;
+ while (i < cbRight)
+ {
+ if (pabRight[i] == 0xff)
+ break;
+ if (cbLeft <= i || pabLeft[i] != pabRight[i])
+ return false;
+ i++;
+ }
+ *pcchMatch = i;
+ return true;
+}
+
+
+/**
+ * Finds a the set with a matching abEscLoad* escape sequence and loads it into
+ * the designated register.
+ *
+ * @returns The length of the sequence on success, negative error status code on
+ * failure.
+ * @param pThis The decoder instance.
+ * @param ppMapRet Used to specify C0 or C1 maps when processing
+ * escape sequences for loading these. Only the
+ * abEscLoadXX arrays will be searched if this is
+ * not NULL. For loading {G0,...,G3} pass NULL.
+ * @param pb Pointer to the start of the escape sequence.
+ * @param cb The number of bytes remaining in the string.
+ * @param papMaps The maps to search.
+ * @param cMaps The number of maps @a papMaps points to.
+ */
+static int rtIso2022Decoder_FindEscAndSet(PRTISO2022DECODERSTATE pThis,
+ PCRTISO2022MAP *ppMapRet, PCRTISO2022MAP *papMaps, uint32_t cMaps)
+{
+ /* Skip the ESC.*/
+ uint8_t const *pb = &pThis->pabString[pThis->offString + 1];
+ uint32_t cb = pThis->cbString - (pThis->offString + 1);
+
+ /* Cache the first char. */
+ uint8_t const b0 = pb[0];
+
+ /* Scan the array of maps for matching sequences. */
+ uint32_t i = cMaps;
+ while (i-- > 0)
+ {
+ uint32_t cchMatch = 0; /* (MSC maybe used uninitialized) */
+ PCRTISO2022MAP pMap = papMaps[i];
+ /** @todo skip non-Teletex codesets if we ever add more than we need for it. */
+ if ( pMap->abEscLoadXX[0] == b0
+ && rtIso2022Decoder_MatchEscSeqFrom2ndByte(pb, cb, pMap->abEscLoadXX, sizeof(pMap->abEscLoadXX), &cchMatch) )
+ {
+ if (ppMapRet)
+ *ppMapRet = pMap;
+ else
+ pThis->apMapGn[0] = pMap;
+ return cchMatch + 1;
+ }
+
+ if (!ppMapRet) /* ppMapRet is NULL if Gn. */
+ {
+ uint32_t iGn;
+ if ( pMap->abEscLoadG1[0] == b0
+ && rtIso2022Decoder_MatchEscSeqFrom2ndByte(pb, cb, pMap->abEscLoadG1, sizeof(pMap->abEscLoadG1), &cchMatch))
+ iGn = 1;
+ else if ( pMap->abEscLoadG2[0] == b0
+ && rtIso2022Decoder_MatchEscSeqFrom2ndByte(pb, cb, pMap->abEscLoadG2, sizeof(pMap->abEscLoadG2), &cchMatch))
+ iGn = 2;
+ else if ( pMap->abEscLoadG3[0] == b0
+ && rtIso2022Decoder_MatchEscSeqFrom2ndByte(pb, cb, pMap->abEscLoadG3, sizeof(pMap->abEscLoadG3), &cchMatch))
+ iGn = 3;
+ else
+ iGn = UINT32_MAX;
+ if (iGn != UINT32_MAX)
+ {
+ pThis->apMapGn[iGn] = pMap;
+ return cchMatch + 1;
+ }
+ }
+ }
+ return VERR_ASN1_TELETEX_UNSUPPORTED_CHARSET;
+}
+
+
+/**
+ * Interprets an escape sequence.
+ *
+ * @returns The length of the sequence on success, negative error status code on
+ * failure.
+ * @param pThis The decoder instance. The offString must be
+ * pointing to the escape byte.
+ */
+static int rtIso2022Decoder_InterpretEsc(PRTISO2022DECODERSTATE pThis)
+{
+ /* the first escape byte. */
+ uint32_t offString = pThis->offString;
+ if (offString + 1 >= pThis->cbString)
+ return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
+ "@%u: Unexpected EOS parsing ESC...", offString);
+ int rc;
+ switch (pThis->pabString[offString + 1])
+ {
+ /*
+ * GL selection:
+ */
+ case 0x6e: /* Lock shift two: G2 -> GL */
+ rc = rtIso2022Decoder_SetGL(pThis, pThis->apMapGn[2]);
+ break;
+ case 0x6f: /* Lock shift three: G3 -> GL */
+ rc = rtIso2022Decoder_SetGL(pThis, pThis->apMapGn[3]);
+ break;
+ case 0x4e: /* Single shift two: G2 -> GL for one char. */
+ rc = rtIso2022Decoder_SetGLForOneChar(pThis, pThis->apMapGn[2]);
+ break;
+ case 0x4f: /* Single shift three: G3 -> GL for one char. */
+ rc = rtIso2022Decoder_SetGLForOneChar(pThis, pThis->apMapGn[3]);
+ break;
+
+ /*
+ * GR selection:
+ */
+ case 0x7e: /* Locking shift one right: G1 -> GR. */
+ rc = rtIso2022Decoder_SetGR(pThis, pThis->apMapGn[1]);
+ break;
+ case 0x7d: /* Locking shift two right: G2 -> GR. */
+ rc = rtIso2022Decoder_SetGR(pThis, pThis->apMapGn[2]);
+ break;
+ case 0x7c: /* Locking shift three right: G3 -> GR. */
+ rc = rtIso2022Decoder_SetGR(pThis, pThis->apMapGn[3]);
+ break;
+
+ /*
+ * Cx selection:
+ */
+ case 0x21: /* C0-designate */
+ return rtIso2022Decoder_FindEscAndSet(pThis, &pThis->pMapC0, g_paC0Maps, RT_ELEMENTS(g_paC0Maps));
+ case 0x22: /* C1-designate */
+ return rtIso2022Decoder_FindEscAndSet(pThis, &pThis->pMapC1, g_paC1Maps, RT_ELEMENTS(g_paC1Maps));
+
+ /*
+ * Single-byte character set selection.
+ */
+ case 0x28: /* G0-designate, 94 chars. */
+ case 0x29: /* G1-designate, 94 chars. */
+ case 0x2a: /* G2-designate, 94 chars. */
+ case 0x2b: /* G3-designate, 94 chars. */
+ return rtIso2022Decoder_FindEscAndSet(pThis, NULL, g_paGLMaps, RT_ELEMENTS(g_paGLMaps));
+
+ case 0x2c: /* G0-designate, 96 chars. */
+ case 0x2d: /* G1-designate, 96 chars. */
+ case 0x2e: /* G2-designate, 96 chars. */
+ case 0x2f: /* G3-designate, 96 chars. */
+ return rtIso2022Decoder_FindEscAndSet(pThis, NULL, g_paGRMaps, RT_ELEMENTS(g_paGRMaps));
+
+ /*
+ * Multibyte character set selection.
+ */
+ case 0x24:
+ if (offString + 2 >= pThis->cbString)
+ return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
+ "@%u: Unexpected EOS parsing ESC %#x...", offString, pThis->pabString[offString + 1]);
+ switch (pThis->pabString[offString + 2])
+ {
+ case 0x28: /* G0-designate, 94^n chars. */
+ case 0x29: /* G1-designate, 94^n chars. */
+ case 0x2a: /* G2-designate, 94^n chars. */
+ case 0x2b: /* G3-designate, 94^n chars. */
+ default: /* G0-designate that skips the 0x28? (See japanese ones.) */
+ return rtIso2022Decoder_FindEscAndSet(pThis, NULL, g_paGLMaps, RT_ELEMENTS(g_paGLMaps));
+
+ case 0x2c: /* G0-designate, 96^n chars. */
+ case 0x2d: /* G1-designate, 96^n chars. */
+ case 0x2e: /* G2-designate, 96^n chars. */
+ case 0x2f: /* G3-designate, 96^n chars. */
+ return rtIso2022Decoder_FindEscAndSet(pThis, NULL, g_paGRMaps, RT_ELEMENTS(g_paGRMaps));
+ } \
+ break;
+
+ case 0x26: /* Special escape prefix for #168. */
+ return rtIso2022Decoder_FindEscAndSet(pThis, NULL, g_paGLMaps, RT_ELEMENTS(g_paGLMaps));
+
+ /*
+ * Unknown/unsupported/unimplemented.
+ */
+ case 0x25: /* Designate other coding system. */
+ return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_TELETEX_UNSUPPORTED_ESC_SEQ,
+ "@%u: ESC DOCS not supported\n", offString);
+ default:
+ return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_TELETEX_UNKNOWN_ESC_SEQ,
+ "@%u: Unknown escape sequence: ESC %#x...\n", offString, pThis->pabString[offString + 1]);
+ }
+
+ /* Only single byte escapes sequences for shifting ends up here. */
+ if (RT_SUCCESS(rc))
+ return 1;
+ return rc;
+}
+
+
+static int rtIso2022Decoder_ControlCharHook(PRTISO2022DECODERSTATE pThis, uint16_t wcControl)
+{
+ int rc;
+ switch (wcControl)
+ {
+ case 0x000e: /* Locking shift zero: G0 -> GL. */
+ rc = rtIso2022Decoder_SetGL(pThis, pThis->apMapGn[0]);
+ break;
+
+ case 0x000f: /* Locking shift one: G1 -> GL. */
+ rc = rtIso2022Decoder_SetGL(pThis, pThis->apMapGn[1]);
+ break;
+
+ case 0x008e: /* Single shift two: G2 -> GL for one char. */
+ rc = rtIso2022Decoder_SetGLForOneChar(pThis, pThis->apMapGn[2]);
+ break;
+
+ case 0x008f: /* Single shift three: G3 -> GL for one char. */
+ rc = rtIso2022Decoder_SetGLForOneChar(pThis, pThis->apMapGn[3]);
+ break;
+
+ case 0x002b: /* Escape should be handled by the caller. */
+ rc = rtIso2022Decoder_InterpretEsc(pThis);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return RT_SUCCESS(rc) ? 1 : rc;
+}
+
+
+static int rtIso2022Decoder_Init(PRTISO2022DECODERSTATE pThis, const char *pchString, uint32_t cchString,
+ uint32_t uGL, uint32_t uC0, uint32_t uC1, uint32_t uG0,
+ PRTERRINFO pErrInfo)
+{
+ pThis->pabString = (uint8_t const *)pchString;
+ pThis->cbString = cchString;
+ pThis->offString = 0;
+
+ pThis->pMapGL = &g_DummyMap;
+ pThis->pMapGR = &g_DummyMap;
+ pThis->pMapC0 = &g_DummyMap;
+ pThis->pMapC1 = &g_DummyMap;
+ pThis->pRestoreGL = NULL;
+ pThis->apMapGn[0] = &g_DummyMap;
+ pThis->apMapGn[1] = &g_DummyMap;
+ pThis->apMapGn[2] = &g_DummyMap;
+ pThis->apMapGn[3] = &g_DummyMap;
+ pThis->pErrInfo = pErrInfo;
+
+ int rc = VINF_SUCCESS;
+ if (uGL != UINT32_MAX)
+ rc = rtIso2022Decoder_LookupAndSet(&pThis->pMapGL, uGL, g_paGLMaps, RT_ELEMENTS(g_paGLMaps));
+ if (RT_SUCCESS(rc) && uG0 != UINT32_MAX)
+ rc = rtIso2022Decoder_LookupAndSet(&pThis->apMapGn[0], uG0, g_paGLMaps, RT_ELEMENTS(g_paGLMaps));
+ if (RT_SUCCESS(rc) && uC0 != UINT32_MAX)
+ rc = rtIso2022Decoder_SetC0(pThis, uC0);
+ if (RT_SUCCESS(rc) && uC1 != UINT32_MAX)
+ rc = rtIso2022Decoder_SetC1(pThis, uC1);
+ return rc;
+}
+
+
+static int rtIso2022Decoder_GetNextUniCpSlow(PRTISO2022DECODERSTATE pThis, PRTUNICP pUniCp)
+{
+ while (pThis->offString < pThis->cbString)
+ {
+ uint8_t b = pThis->pabString[pThis->offString];
+ if (!(b & 0x80))
+ {
+ if (b >= 0x20)
+ {
+ /*
+ * GL range.
+ */
+ b -= 0x20;
+ PCRTISO2022MAP pMap = pThis->pMapGL;
+
+ /* Single byte character map. */
+ if (pMap->cb == 1)
+ {
+ if (RT_LIKELY(b < pMap->cToUni))
+ {
+ uint16_t wc = pMap->pauToUni[b];
+ if (RT_LIKELY(wc != RTISO2022_UNUSED))
+ {
+ *pUniCp = wc;
+ pThis->offString += 1;
+ return VINF_SUCCESS;
+ }
+ *pUniCp = RTUNICP_INVALID;
+ return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
+ "@%u: GL b=%#x is marked unused in map #%u range %u.",
+ pThis->offString, b + 0x20, pMap->uRegistration, pMap->cToUni);
+ }
+ *pUniCp = RTUNICP_INVALID;
+ return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
+ "@%u: GL b=%#x is outside map #%u range %u.",
+ pThis->offString, b + 0x20, pMap->uRegistration, pMap->cToUni);
+ }
+
+ /* Double byte character set. */
+ Assert(pMap->cb == 2);
+ if (pThis->offString + 1 < pThis->cbString)
+ {
+ uint8_t b2 = pThis->pabString[pThis->offString + 1];
+ b2 -= 0x20;
+ if (RT_LIKELY(b2 < 0x60))
+ {
+ uint16_t u16 = ((uint16_t)b << 8) | b2;
+ if (RT_LIKELY(u16 < pMap->cToUni))
+ {
+ uint16_t wc = pMap->pauToUni[b];
+ if (RT_LIKELY(wc != RTISO2022_UNUSED))
+ {
+ *pUniCp = wc;
+ pThis->offString += 2;
+ return VINF_SUCCESS;
+ }
+ *pUniCp = RTUNICP_INVALID;
+ return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
+ "@%u: GL b=%#x is marked unused in map #%u.",
+ pThis->offString, b + 0x20, pMap->uRegistration);
+ }
+ if (u16 >= 0x7f00)
+ {
+ *pUniCp = 0x7f; /* delete */
+ pThis->offString += 2;
+ return VINF_SUCCESS;
+ }
+ *pUniCp = RTUNICP_INVALID;
+ return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
+ "@%u: GL u16=%#x (b0=%#x b1=%#x) is outside map #%u range %u.",
+ pThis->offString, u16, b + 0x20, b2 + 0x20, pMap->uRegistration, pMap->cToUni);
+ }
+ return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
+ "@%u: 2nd GL byte outside GL range: b0=%#x b1=%#x (map #%u)",
+ pThis->offString, b + 0x20, b2 + 0x20, pMap->uRegistration);
+
+ }
+ return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
+ "@%u: EOS reading 2nd byte for GL b=%#x (map #%u).",
+ pThis->offString, b + 0x20, pMap->uRegistration);
+ }
+ else
+ {
+ /*
+ * C0 range.
+ */
+ Assert(pThis->pMapC0->cb == 0x20);
+ uint16_t wc = pThis->pMapC0->pauToUni[b];
+ if (wc != RTISO2022_UNUSED)
+ {
+ int rc;
+ if (b == 0x1b || wc == 0x1b) /* ESC is hardcoded, or so they say. */
+ rc = rtIso2022Decoder_InterpretEsc(pThis);
+ else
+ rc = rtIso2022Decoder_ControlCharHook(pThis, wc);
+ if (RT_SUCCESS(rc))
+ {
+ if (rc == 0)
+ {
+ pThis->offString += 1;
+ *pUniCp = wc;
+ return VINF_SUCCESS;
+ }
+ pThis->offString += rc;
+ }
+ else
+ return rc;
+ }
+ else
+ return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
+ "@%u: C0 b=%#x is marked unused in map #%u.",
+ pThis->offString, b, pThis->pMapC0->uRegistration);
+ }
+ }
+ else
+ {
+ if (b >= 0xa0)
+ {
+ /*
+ * GR range.
+ */
+ b -= 0xa0;
+ PCRTISO2022MAP pMap = pThis->pMapGR;
+
+ /* Single byte character map. */
+ if (pMap->cb == 1)
+ {
+ /** @todo 0xa0 = SPACE and 0xff = DELETE if it's a 94 charater map... */
+ if (RT_LIKELY(b < pMap->cToUni))
+ {
+ uint16_t wc = pMap->pauToUni[b];
+ if (RT_LIKELY(wc != RTISO2022_UNUSED))
+ {
+ *pUniCp = wc;
+ pThis->offString += 1;
+ return VINF_SUCCESS;
+ }
+
+ *pUniCp = RTUNICP_INVALID;
+ return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
+ "@%u: GR b=%#x is marked unused in map #%u.",
+ pThis->offString, b + 0xa0, pMap->uRegistration);
+ }
+ *pUniCp = RTUNICP_INVALID;
+ return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
+ "@%u: GR b=%#x is outside map #%u range %u",
+ pThis->offString, b + 0xa0, pMap->uRegistration, pMap->cToUni);
+ }
+
+ /* Double byte character set. */
+ Assert(pMap->cb == 2);
+ if (pThis->offString + 1 < pThis->cbString)
+ {
+ uint8_t b2 = pThis->pabString[pThis->offString + 1];
+ b2 -= 0xa0;
+ if (RT_LIKELY(b2 < 0x60))
+ {
+ uint16_t u16 = ((uint16_t)b << 8) | b2;
+ if (RT_LIKELY(u16 < pMap->cToUni))
+ {
+ uint16_t wc = pMap->pauToUni[b];
+ if (RT_LIKELY(wc != RTISO2022_UNUSED))
+ {
+ *pUniCp = wc;
+ pThis->offString += 2;
+ return VINF_SUCCESS;
+ }
+
+ *pUniCp = RTUNICP_INVALID;
+ return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
+ "@%u: GR b=%#x is marked unused in map #%u.",
+ pThis->offString, b + 0xa0, pMap->uRegistration);
+ }
+ *pUniCp = RTUNICP_INVALID;
+ return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
+ "@%u: GR u16=%#x (b0=%#x b1=%#x) is outside map #%u range %u.",
+ pThis->offString, u16, b + 0xa0, b2 + 0xa0, pMap->uRegistration, pMap->cToUni);
+ }
+ return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
+ "@%u: 2nd GR byte outside GR range: b0=%#x b1=%#x (map #%u).",
+ pThis->offString, b + 0xa0, b2 + 0xa0, pMap->uRegistration);
+
+ }
+ return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
+ "@%u: EOS reading 2nd byte for GR b=%#x (map #%u).",
+ pThis->offString, b + 0xa0, pMap->uRegistration);
+ }
+ else
+ {
+ /*
+ * C2 range.
+ */
+ Assert(pThis->pMapC1->cb == 0x20);
+ b -= 0x80;
+ uint16_t wc = pThis->pMapC1->pauToUni[b];
+ if (wc != RTISO2022_UNUSED)
+ {
+ int rc = rtIso2022Decoder_ControlCharHook(pThis, wc);
+ if (RT_SUCCESS(rc))
+ {
+ if (rc == 0)
+ {
+ pThis->offString += 1;
+ *pUniCp = wc;
+ return VINF_SUCCESS;
+ }
+ pThis->offString += rc;
+ }
+ else
+ return rc;
+ }
+ else
+ return RTErrInfoSetF(pThis->pErrInfo, VERR_ASN1_INVALID_T61_STRING_ENCODING,
+ "@%u: C1 b=%#x is marked unused in map #%u.",
+ pThis->offString, b + 0x80, pThis->pMapC1->uRegistration);
+ }
+ }
+ }
+
+ /* End of string. */
+ *pUniCp = RTUNICP_INVALID;
+ return VERR_END_OF_STRING;
+}
+
+DECLINLINE(int) rtIso2022Decoder_GetNextUniCp(PRTISO2022DECODERSTATE pThis, PRTUNICP pUniCp)
+{
+ /*
+ * Deal with single byte GL.
+ */
+ uint32_t const offString = pThis->offString;
+ if (pThis->offString < pThis->cbString)
+ {
+ PCRTISO2022MAP const pMapGL = pThis->pMapGL;
+ if (pMapGL->cb == 1)
+ {
+ uint8_t const b = pThis->pabString[offString] - (uint8_t)0x20;
+ if (b < pMapGL->cToUni)
+ {
+ uint16_t wc = pMapGL->pauToUni[b];
+ if (wc != RTISO2022_UNUSED)
+ {
+ pThis->offString = offString + 1;
+ *pUniCp = wc;
+ return VINF_SUCCESS;
+ }
+ }
+ }
+
+ /*
+ * Deal with complications in the non-inline function.
+ */
+ return rtIso2022Decoder_GetNextUniCpSlow(pThis, pUniCp);
+ }
+
+ *pUniCp = RTUNICP_INVALID;
+ return VERR_END_OF_STRING;
+}
+
+
+static int rtIso2022ValidateString(uint32_t uProfile, const char *pch, uint32_t cch, size_t *pcchUtf8, PRTERRINFO pErrInfo)
+{
+ AssertReturn(uProfile == ASN1_TAG_T61_STRING, VERR_INVALID_PARAMETER); /* just a place holder for now. */
+
+ RTISO2022DECODERSTATE Decoder;
+ int rc = rtIso2022Decoder_Init(&Decoder, pch, cch, 102, 106, 107, 102, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cchUtf8 = 0;
+ for (;;)
+ {
+ RTUNICP uc;
+ rc = rtIso2022Decoder_GetNextUniCp(&Decoder, &uc);
+ if (RT_SUCCESS(rc))
+ cchUtf8 += RTStrCpSize(uc);
+ else
+ {
+ if (RT_LIKELY(rc == VERR_END_OF_STRING))
+ {
+ *pcchUtf8 = cchUtf8;
+ return VINF_SUCCESS;
+ }
+ return rc;
+ }
+ }
+ }
+ return rc;
+}
+
+
+static int rtIso2022RecodeAsUtf8(uint32_t uProfile, const char *pchSrc, uint32_t cchSrc, char *pszDst, size_t cbDst)
+{
+ AssertReturn(uProfile == ASN1_TAG_T61_STRING, VERR_INVALID_PARAMETER); /* just a place holder for now. */
+ AssertReturn(cbDst > 0, VERR_INVALID_PARAMETER);
+
+ RTISO2022DECODERSTATE Decoder;
+ int rc = rtIso2022Decoder_Init(&Decoder, pchSrc, cchSrc, 102, 106, 107, 102, NULL /*pErrInfo*/);
+ if (RT_SUCCESS(rc))
+ {
+ for (;;)
+ {
+ RTUNICP uc;
+ rc = rtIso2022Decoder_GetNextUniCp(&Decoder, &uc);
+ if (RT_SUCCESS(rc))
+ {
+ if (uc < 0x80 && cbDst > 1)
+ {
+ *pszDst++ = (char)uc;
+ cbDst--;
+ }
+ else
+ {
+ size_t cchUniCp = RTStrCpSize(uc);
+ if (cbDst > cchUniCp)
+ {
+ cbDst -= cchUniCp;
+ pszDst = RTStrPutCp(pszDst, uc);
+ }
+ else
+ {
+ *pszDst = '\0';
+ return VERR_BUFFER_OVERFLOW;
+ }
+ }
+ }
+ else if (RT_LIKELY(rc == VERR_END_OF_STRING))
+ {
+ *pszDst = '\0';
+ return VINF_SUCCESS;
+ }
+ else
+ return rc;
+ }
+ }
+ return rc;
+}
+
+
+
+/** The unicode mapping of the C1 area of windows codepage 1252.
+ * The rest of the code page is 1:1 with unicode. */
+static uint16_t g_awcWin1252_C1[0x20] =
+{
+ 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f,
+ 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178,
+};
+
+
+static size_t rtWin1252CalcUtf8Length(const char *pch, uint32_t cch)
+{
+ size_t cchUtf8 = 0;
+ while (cch-- > 0)
+ {
+ uint8_t const b = *pch++;
+ if (b < 0x80)
+ cchUtf8 += 1;
+ else if (b >= 0xa0)
+ cchUtf8 += 2;
+ else
+ {
+ uint16_t const wc = g_awcWin1252_C1[b - 0x80];
+ cchUtf8 += RTStrCpSize(wc);
+ }
+ }
+ return cchUtf8;
+}
+
+
+static int rtWin1252RecodeAsUtf8(const char *pchSrc, uint32_t cchSrc, char *pszDst, size_t cbDst)
+{
+ while (cchSrc-- > 0)
+ {
+ uint8_t b = *pchSrc++;
+ if (b < 0x80)
+ {
+ if (cbDst <= 1)
+ return VERR_BUFFER_OVERFLOW;
+ *pszDst++ = (char)b;
+ }
+ else
+ {
+ uint16_t const wc = b >= 0xa0 ? b : g_awcWin1252_C1[b - 0x80];
+ size_t cchCp = RTStrCpSize(wc);
+ if (cbDst <= cchCp)
+ return VERR_BUFFER_OVERFLOW;
+ pszDst = RTStrPutCp(pszDst, wc);
+ }
+ }
+
+ if (!cbDst)
+ return VERR_BUFFER_OVERFLOW;
+ *pszDst = '\0';
+ return VINF_SUCCESS;
+}
+
+
+
+/*
+ * ASN.1 STRING - Specific Methods.
+ */
+
+/** rtAsn1String_IsTeletexLatin1 results. */
+typedef enum RTASN1TELETEXVARIANT
+{
+ /** Couldn't find hard evidence of either. */
+ RTASN1TELETEXVARIANT_UNDECIDED = 1,
+ /** Pretty certain that it's real teletex. */
+ RTASN1TELETEXVARIANT_TELETEX,
+ /** Pretty sure it's latin-1 or Windows-1252. */
+ RTASN1TELETEXVARIANT_LATIN1,
+ /** Pretty sure it's Windows-1252. */
+ RTASN1TELETEXVARIANT_WIN_1252
+} RTASN1TELETEXVARIANT;
+
+/**
+ * Takes a guess as whether TELETEX STRING (T61 STRING) is actually is Latin-1
+ * or the real thing.
+ *
+ * According to RFC-2459, section 4.1.2.4, various libraries, certificate
+ * authorities and others have perverted the TeletexString/T61String tag by
+ * ISO-8859-1 (aka latin-1) strings (more probably these are actually Windows
+ * CP-1252 rather than latin-1). We'll try detect incompatible latin-1
+ * perversions by:
+ * - The use of GR (0xf0-0xff) chars.
+ * - The lack of ESC sequences and shifts (LS0,LS1,SS2,SS3)
+ *
+ * An ASSUMTION here is that GR is not loaded with anything at the start of a
+ * teletex string, as per table 3 in section 8.23.5.2 in T-REC-X.590.200811.
+ *
+ * @retval @c true if chances are good that it's LATIN-1.
+ * @retval @c false if changes are very good that it's real teletex.
+ * @param pch The first char in the string.
+ * @param cch The string length.
+ *
+ * @remarks Useful info on Teletex and ISO/IEC-2022:
+ * https://www.mail-archive.com/asn1@asn1.org/msg00460.html
+ * http://en.wikipedia.org/wiki/ISO/IEC_2022
+ * http://www.open-std.org/cen/tc304/guide/GCONCEPT.HTM
+ * http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-035.pdf
+ */
+static RTASN1TELETEXVARIANT rtAsn1String_IsTeletexLatin1(const char *pch, uint32_t cch)
+{
+ RTASN1TELETEXVARIANT enmVariant = RTASN1TELETEXVARIANT_UNDECIDED;
+ while (cch-- > 0)
+ {
+ uint8_t const b = *pch;
+ if (b >= 0x20 && b <= 0x7f)
+ {
+ if (g_awcIsoIr102Decode[b - 0x20] == RTISO2022_UNUSED)
+ enmVariant = RTASN1TELETEXVARIANT_LATIN1;
+ }
+ else
+ {
+ if ( b == 0x1b /* ESC */
+ || b == 0x0e /* LS0 / SI */
+ || b == 0x0f /* LS1 / SO */
+ || b == 0x19 /* SS2 */
+ || b == 0x1d /* SS3 */ )
+ return RTASN1TELETEXVARIANT_TELETEX;
+
+ if (b >= 0xa0)
+ enmVariant = RTASN1TELETEXVARIANT_LATIN1;
+ else if (b >= 0x80 && b <= 0x9f)
+ {
+ /* Any use of C1 characters defined by windows cp-1252 will
+ lead us to believe it's the windows code rather than the
+ ISO/IEC standard that is being used. (Not that it makes
+ much of a difference, because we're gonna treat it as the
+ windows codepage, anyways.) */
+ if ( b != 0x81
+ && b != 0x8d
+ && b != 0x8f
+ && b != 0x90
+ && b != 0x9d)
+ return RTASN1TELETEXVARIANT_WIN_1252;
+ }
+ }
+ }
+ return RTASN1TELETEXVARIANT_UNDECIDED;
+}
+
+
+/**
+ * Checks the encoding of an ASN.1 string according to it's tag.
+ *
+ * @returns IPRT status code.
+ * @param pThis The string to santity check.
+ * @param pErrInfo Where to store extra error info. Optional.
+ * @param pcchUtf8 Where to return the UTF-8 string length. Optional.
+ */
+static int rtAsn1String_CheckSanity(PCRTASN1STRING pThis, PRTERRINFO pErrInfo, const char *pszErrorTag, size_t *pcchUtf8)
+{
+ int rc;
+ uint32_t cch = pThis->Asn1Core.cb;
+ size_t cchUtf8 = cch;
+ const char *pch = pThis->Asn1Core.uData.pch;
+ uint32_t uTag = RTASN1CORE_GET_TAG(&pThis->Asn1Core);
+ switch (uTag)
+ {
+ case ASN1_TAG_UTF8_STRING:
+ rc = RTStrValidateEncodingEx(pch, cch, 0);
+ if (RT_SUCCESS(rc))
+ break;
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_INVALID_UTF8_STRING_ENCODING, "%s: Bad UTF-8 encoding (%Rrc, %.*Rhxs)",
+ pszErrorTag, rc, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
+
+ case ASN1_TAG_NUMERIC_STRING:
+ while (cch-- > 0)
+ {
+ char ch = *pch++;
+ if ( !RT_C_IS_DIGIT(ch)
+ && ch != ' ')
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_INVALID_NUMERIC_STRING_ENCODING,
+ "%s: Bad numeric string: ch=%#x (pos %u in %.*Rhxs)", pszErrorTag, ch,
+ pThis->Asn1Core.cb - cch + 1, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
+ }
+ break;
+
+ case ASN1_TAG_PRINTABLE_STRING:
+ while (cch-- > 0)
+ {
+ char ch = *pch++;
+ if ( !RT_C_IS_ALNUM(ch)
+ && ch != ' '
+ && ch != '\''
+ && ch != '('
+ && ch != ')'
+ && ch != '+'
+ && ch != ','
+ && ch != '-'
+ && ch != '.'
+ && ch != '/'
+ && ch != ':'
+ && ch != '='
+ && ch != '?'
+ )
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_INVALID_PRINTABLE_STRING_ENCODING,
+ "%s: Bad printable string: ch=%#x (pos %u in %.*Rhxs)", pszErrorTag, ch,
+ pThis->Asn1Core.cb - cch + 1, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
+ }
+ break;
+
+ case ASN1_TAG_IA5_STRING: /* ASCII */
+ while (cch-- > 0)
+ {
+ unsigned char ch = *pch++;
+ if (ch == 0 || ch >= 0x80)
+ {
+ /* Ignore C-style zero terminator as the "Microsoft ECC Product Root Certificate Authority 2018"
+ for instance, has a policy qualifier string "http://www.microsoft.com/pkiops/Docs/Repository.htm\0" */
+ /** @todo should '\0' really be excluded above? */
+ if (ch != 0 || cch != 0)
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_INVALID_IA5_STRING_ENCODING,
+ "%s: Bad IA5 string: ch=%#x (pos %u in %.*Rhxs)", pszErrorTag, ch,
+ pThis->Asn1Core.cb - cch + 1, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
+ break;
+ }
+ }
+ break;
+
+ case ASN1_TAG_T61_STRING:
+ switch (rtAsn1String_IsTeletexLatin1(pch, cch))
+ {
+ default:
+ rc = rtIso2022ValidateString(ASN1_TAG_T61_STRING, pch, cch, &cchUtf8, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+ break;
+ case RTASN1TELETEXVARIANT_UNDECIDED:
+ case RTASN1TELETEXVARIANT_LATIN1:
+ case RTASN1TELETEXVARIANT_WIN_1252:
+ cchUtf8 = rtWin1252CalcUtf8Length(pch, cch);
+ break;
+ }
+ break;
+
+ case ASN1_TAG_VIDEOTEX_STRING:
+ case ASN1_TAG_GRAPHIC_STRING:
+ return VERR_ASN1_STRING_TYPE_NOT_IMPLEMENTED;
+
+ case ASN1_TAG_VISIBLE_STRING:
+ while (cch-- > 0)
+ {
+ unsigned char ch = *pch++;
+ if (ch < 0x20 || ch >= 0x7f)
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_INVALID_VISIBLE_STRING_ENCODING,
+ "%s: Bad visible string: ch=%#x (pos %u in %.*Rhxs)", pszErrorTag, ch,
+ pThis->Asn1Core.cb - cch + 1, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
+ }
+ break;
+
+ case ASN1_TAG_GENERAL_STRING:
+ return VERR_ASN1_STRING_TYPE_NOT_IMPLEMENTED;
+
+ case ASN1_TAG_UNIVERSAL_STRING:
+ if (!(cch & 3))
+ {
+ uint8_t const *pb = (uint8_t const *)pch;
+ cchUtf8 = 0;
+ while (cch > 0)
+ {
+ RTUNICP uc = RT_MAKE_U32_FROM_U8(pb[3], pb[2], pb[1], pb[0]); /* big endian */
+ if (!RTUniCpIsValid(uc))
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_INVALID_UNIVERSAL_STRING_ENCODING,
+ "%s: Bad universal string: uc=%#x (pos %u in %.*Rhxs)", pszErrorTag, uc,
+ pThis->Asn1Core.cb - cch + 1, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
+ cchUtf8 += RTUniCpCalcUtf8Len(uc);
+
+ /* next */
+ pb += 4;
+ cch -= 4;
+ }
+ break;
+ }
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_INVALID_UNIVERSAL_STRING_ENCODING,
+ "%s: Bad universal string: size not a multiple of 4: cch=%#x (%.*Rhxs)",
+ pszErrorTag, cch, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
+
+ case ASN1_TAG_BMP_STRING:
+ if (!(cch & 1))
+ {
+ uint8_t const *pb = (uint8_t const *)pch;
+ cchUtf8 = 0;
+ while (cch > 0)
+ {
+ RTUNICP uc = RT_MAKE_U32_FROM_U8(pb[1], pb[0], 0, 0); /* big endian */
+ if (!RTUniCpIsValid(uc))
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_INVALID_BMP_STRING_ENCODING,
+ "%s: Bad BMP string: uc=%#x (pos %u in %.*Rhxs)", pszErrorTag, uc,
+ pThis->Asn1Core.cb - cch + 1, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
+ cchUtf8 += RTUniCpCalcUtf8Len(uc);
+
+ /* next */
+ pb += 2;
+ cch -= 2;
+ }
+ break;
+ }
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_INVALID_BMP_STRING_ENCODING,
+ "%s: Bad BMP string: odd number of bytes cch=%#x (%.*Rhxs)",
+ pszErrorTag, cch, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
+
+ default:
+ AssertMsgFailedReturn(("uTag=%#x\n", uTag), VERR_INTERNAL_ERROR_3);
+ }
+
+ if (pcchUtf8)
+ *pcchUtf8 = cchUtf8;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1String_CompareValues(PCRTASN1STRING pLeft, PCRTASN1STRING pRight)
+{
+ return RTAsn1String_CompareEx(pLeft, pRight, false /*fTypeToo*/);
+}
+
+
+RTDECL(int) RTAsn1String_CompareEx(PCRTASN1STRING pLeft, PCRTASN1STRING pRight, bool fTypeToo)
+{
+ Assert(pLeft && (!RTAsn1String_IsPresent(pLeft) || pLeft->Asn1Core.pOps == &g_RTAsn1String_Vtable));
+ Assert(pRight && (!RTAsn1String_IsPresent(pRight) || pRight->Asn1Core.pOps == &g_RTAsn1String_Vtable));
+
+ int iDiff;
+ if (RTAsn1String_IsPresent(pLeft))
+ {
+ if (RTAsn1String_IsPresent(pRight))
+ {
+ if (!fTypeToo || RTASN1CORE_GET_TAG(&pLeft->Asn1Core) == RTASN1CORE_GET_TAG(&pRight->Asn1Core))
+ iDiff = RTAsn1Core_CompareEx(&pLeft->Asn1Core, &pRight->Asn1Core, true /*fIgnoreTagAndClass*/);
+ else
+ iDiff = RTASN1CORE_GET_TAG(&pLeft->Asn1Core) < RTASN1CORE_GET_TAG(&pRight->Asn1Core) ? -1 : 1;
+ }
+ else
+ iDiff = 1;
+ }
+ else
+ iDiff = 0 - RTAsn1String_IsPresent(pRight);
+ return iDiff;
+}
+
+
+RTDECL(int) RTAsn1String_CompareWithString(PCRTASN1STRING pThis, const char *pszString, size_t cchString)
+{
+ Assert(pThis && (!RTAsn1String_IsPresent(pThis) || pThis->Asn1Core.pOps == &g_RTAsn1String_Vtable));
+ AssertPtr(pszString);
+
+ int iDiff;
+ if (RTAsn1String_IsPresent(pThis))
+ {
+ if (cchString == RTSTR_MAX)
+ cchString = strlen(pszString);
+
+ /*
+ * If there is a UTF-8 conversion available already, use it.
+ */
+ if (pThis->pszUtf8)
+ {
+ iDiff = strncmp(pThis->pszUtf8, pszString, cchString);
+ if (!iDiff && pThis->cchUtf8 != cchString)
+ iDiff = pThis->cchUtf8 < cchString ? -1 : 1;
+ }
+ else
+ {
+ /*
+ * Some types are UTF-8 compatible, so try do the compare without
+ * RTAsn1String_QueryUtf8.
+ */
+ uint32_t cch = pThis->Asn1Core.cb;
+ const char *pch = pThis->Asn1Core.uData.pch;
+ switch (RTASN1CORE_GET_TAG(&pThis->Asn1Core))
+ {
+ case ASN1_TAG_UTF8_STRING:
+ case ASN1_TAG_NUMERIC_STRING:
+ case ASN1_TAG_IA5_STRING:
+ case ASN1_TAG_PRINTABLE_STRING:
+ iDiff = strncmp(pch, pszString, RT_MIN(cch, cchString));
+ if (iDiff && cch != cchString)
+ iDiff = cch < cchString ? - 1 : 1;
+ break;
+
+ /** @todo Implement comparing ASN1_TAG_BMP_STRING, ASN1_TAG_UNIVERSAL_STRING and
+ * ASN1_TAG_T61_STRING with UTF-8 strings without conversion. */
+
+ default:
+ {
+ int rc = RTAsn1String_QueryUtf8(pThis, NULL, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ iDiff = strncmp(pThis->pszUtf8, pszString, cchString);
+ if (!iDiff && pThis->cchUtf8 != cchString)
+ iDiff = pThis->cchUtf8 < cchString ? -1 : 1;
+ }
+ else
+ iDiff = -1;
+ break;
+ }
+ }
+ }
+
+ /* Reduce the strcmp return value. */
+ if (iDiff != 0)
+ iDiff = iDiff < 0 ? -1 : 1;
+ }
+ else
+ iDiff = -1;
+ return iDiff;
+}
+
+
+RTDECL(int) RTAsn1String_QueryUtf8(PCRTASN1STRING pThis, const char **ppsz, size_t *pcch)
+{
+ Assert(pThis->Asn1Core.pOps == &g_RTAsn1String_Vtable);
+
+ char *psz = (char *)pThis->pszUtf8;
+ size_t cch = pThis->cchUtf8;
+ if (!psz)
+ {
+
+ /*
+ * Convert the first time around. Start by validating the encoding and
+ * calculating the length.
+ */
+ int rc = rtAsn1String_CheckSanity(pThis, NULL, NULL, &cch);
+ if (RT_SUCCESS(rc))
+ {
+ PRTASN1STRING pThisNC = (PRTASN1STRING)pThis;
+ rc = RTAsn1MemAllocZ(&pThisNC->Allocation, (void **)&psz, cch + 1);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Got memory, now do the actual convertion to UTF-8 / copying.
+ */
+ switch (RTASN1CORE_GET_TAG(&pThis->Asn1Core))
+ {
+ case ASN1_TAG_UTF8_STRING:
+ case ASN1_TAG_NUMERIC_STRING:
+ case ASN1_TAG_PRINTABLE_STRING:
+ case ASN1_TAG_IA5_STRING:
+ case ASN1_TAG_VISIBLE_STRING:
+ Assert(cch == pThis->Asn1Core.cb);
+ memcpy(psz, pThis->Asn1Core.uData.pch, cch);
+ psz[cch] = '\0';
+ break;
+
+ case ASN1_TAG_T61_STRING:
+ switch (rtAsn1String_IsTeletexLatin1(pThis->Asn1Core.uData.pch, pThis->Asn1Core.cb))
+ {
+ default:
+ rc = rtIso2022RecodeAsUtf8(ASN1_TAG_T61_STRING, pThis->Asn1Core.uData.pch, pThis->Asn1Core.cb,
+ psz, cch + 1);
+ break;
+ case RTASN1TELETEXVARIANT_UNDECIDED:
+ case RTASN1TELETEXVARIANT_LATIN1:
+ case RTASN1TELETEXVARIANT_WIN_1252:
+ rc = rtWin1252RecodeAsUtf8(pThis->Asn1Core.uData.pch, pThis->Asn1Core.cb, psz, cch + 1);
+ break;
+ }
+ AssertReturnStmt(RT_SUCCESS(rc), RTAsn1MemFree(&pThisNC->Allocation, psz), VERR_INTERNAL_ERROR_3);
+ break;
+
+ /* case ASN1_TAG_VIDEOTEX_STRING: */
+ /* case ASN1_TAG_GRAPHIC_STRING: */
+ /* case ASN1_TAG_GENERAL_STRING: */
+
+ case ASN1_TAG_UNIVERSAL_STRING:
+ {
+ char *pszDst = psz;
+ size_t cchSrc = pThis->Asn1Core.cb;
+ uint8_t const *pbSrc = pThis->Asn1Core.uData.pu8;
+ while (cchSrc > 0)
+ {
+ RTUNICP uc = RT_MAKE_U32_FROM_U8(pbSrc[3], pbSrc[2], pbSrc[1], pbSrc[0]); /* big endian */
+ AssertReturnStmt(RTUniCpIsValid(uc), RTAsn1MemFree(&pThisNC->Allocation, psz), VERR_INTERNAL_ERROR_2);
+ pszDst = RTStrPutCp(pszDst, uc);
+
+ /* next */
+ pbSrc += 4;
+ cchSrc -= 4;
+ }
+ Assert((size_t)(pszDst - psz) == cch);
+ break;
+ }
+
+ case ASN1_TAG_BMP_STRING:
+ {
+ char *pszDst = psz;
+ size_t cchSrc = pThis->Asn1Core.cb;
+ uint8_t const *pbSrc = pThis->Asn1Core.uData.pu8;
+ while (cchSrc > 0)
+ {
+ RTUNICP uc = RT_MAKE_U32_FROM_U8(pbSrc[1], pbSrc[0], 0, 0); /* big endian */
+ AssertReturnStmt(RTUniCpIsValid(uc), RTAsn1MemFree(&pThisNC->Allocation, psz), VERR_INTERNAL_ERROR_2);
+ pszDst = RTStrPutCp(pszDst, uc);
+
+ /* next */
+ pbSrc += 2;
+ cchSrc -= 2;
+ }
+ Assert((size_t)(pszDst - psz) == cch);
+ break;
+ }
+
+ default:
+ RTAsn1MemFree(&pThisNC->Allocation, psz);
+ AssertMsgFailedReturn(("uTag=%#x\n", RTASN1CORE_GET_TAG(&pThis->Asn1Core)), VERR_INTERNAL_ERROR_3);
+ }
+
+ /*
+ * Successfully produced UTF-8. Save it in the object.
+ */
+ pThisNC->pszUtf8 = psz;
+ pThisNC->cchUtf8 = (uint32_t)cch;
+ }
+ else
+ return rc;
+ }
+ else
+ return rc;
+ }
+
+ /*
+ * Success.
+ */
+ if (ppsz)
+ *ppsz = psz;
+ if (pcch)
+ *pcch = cch;
+ return VINF_SUCCESS;
+}
+
+
+
+RTDECL(int) RTAsn1String_QueryUtf8Len(PCRTASN1STRING pThis, size_t *pcch)
+{
+ Assert(pThis->Asn1Core.pOps == &g_RTAsn1String_Vtable);
+
+ size_t cch = pThis->cchUtf8;
+ if (!cch && !pThis->pszUtf8)
+ {
+ int rc = rtAsn1String_CheckSanity(pThis, NULL, NULL, &cch);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ *pcch = cch;
+ return VINF_SUCCESS;
+}
+
+
+
+
+RTDECL(int) RTAsn1String_InitEx(PRTASN1STRING pThis, uint32_t uTag, void const *pvValue, size_t cbValue,
+ PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ RT_ZERO(*pThis);
+ AssertMsgReturn(uTag < RT_ELEMENTS(g_acbStringTags) && g_acbStringTags[uTag] > 0, ("uTag=%#x\n", uTag),
+ VERR_INVALID_PARAMETER);
+
+ RTAsn1MemInitAllocation(&pThis->Allocation, pAllocator);
+ RTAsn1Core_InitEx(&pThis->Asn1Core,
+ uTag,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ &g_RTAsn1String_Vtable,
+ RTASN1CORE_F_PRESENT | RTASN1CORE_F_PRIMITE_TAG_STRUCT);
+
+ if (cbValue > 0)
+ {
+ int rc = RTAsn1ContentDup(&pThis->Asn1Core, pvValue, cbValue, pAllocator);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1String_InitWithValue(PRTASN1STRING pThis, const char *pszUtf8Value, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ Assert(RTStrValidateEncoding(pszUtf8Value));
+ return RTAsn1String_InitEx(pThis, ASN1_TAG_UTF8_STRING, pszUtf8Value, strlen(pszUtf8Value), pAllocator);
+}
+
+
+RTDECL(int) RTAsn1String_RecodeAsUtf8(PRTASN1STRING pThis, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ /*
+ * Query the UTF-8 string. Do this even if it's already an
+ * ASN1_TAG_UTF8_STRING object as it makes sure we've got a valid UTF-8
+ * string upon successful return.
+ */
+ int rc = RTAsn1String_QueryUtf8(pThis, NULL, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (RTASN1CORE_GET_TAG(&pThis->Asn1Core) != ASN1_TAG_UTF8_STRING)
+ {
+ /*
+ * Resize the content, copy the UTF-8 bytes in there, and change
+ * the tag.
+ */
+ rc = RTAsn1ContentReallocZ(&pThis->Asn1Core, pThis->cchUtf8, pAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ memcpy((void *)pThis->Asn1Core.uData.pv, pThis->pszUtf8, pThis->cchUtf8);
+ rc = RTAsn1Core_ChangeTag(&pThis->Asn1Core, ASN1_TAG_UTF8_STRING);
+ }
+ }
+ }
+ return rc;
+}
+
+
+
+/*
+ * ASN.1 STRING - Standard Methods.
+ */
+
+RT_DECL_DATA_CONST(RTASN1COREVTABLE const) g_RTAsn1String_Vtable =
+{
+ "RTAsn1String",
+ sizeof(RTASN1STRING),
+ UINT8_MAX,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ 0,
+ (PFNRTASN1COREVTDTOR)RTAsn1String_Delete,
+ NULL,
+ (PFNRTASN1COREVTCLONE)RTAsn1String_Clone,
+ (PFNRTASN1COREVTCOMPARE)RTAsn1String_Compare,
+ (PFNRTASN1COREVTCHECKSANITY)RTAsn1String_CheckSanity,
+ NULL,
+ NULL
+};
+
+
+RTDECL(int) RTAsn1String_Init(PRTASN1STRING pThis, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ return RTAsn1String_InitEx(pThis, ASN1_TAG_UTF8_STRING, NULL /*pvValue*/, 0 /*cbValue*/, pAllocator);
+}
+
+
+RTDECL(int) RTAsn1String_Clone(PRTASN1STRING pThis, PCRTASN1STRING pSrc, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ AssertPtr(pSrc); AssertPtr(pThis); AssertPtr(pAllocator);
+ RT_ZERO(*pThis);
+ if (RTAsn1String_IsPresent(pSrc))
+ {
+ AssertReturn(pSrc->Asn1Core.pOps == &g_RTAsn1String_Vtable, VERR_INTERNAL_ERROR_3);
+ int rc = RTAsn1Core_CloneContent(&pThis->Asn1Core, &pSrc->Asn1Core, pAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ /* Don't copy the UTF-8 representation, decode it when queried. */
+ RTAsn1MemInitAllocation(&pThis->Allocation, pAllocator);
+ return VINF_SUCCESS;
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(void) RTAsn1String_Delete(PRTASN1STRING pThis)
+{
+ if ( pThis
+ && RTAsn1String_IsPresent(pThis))
+ {
+ Assert(pThis->Asn1Core.pOps == &g_RTAsn1String_Vtable);
+
+ if (pThis->Allocation.cbAllocated)
+ RTAsn1MemFree(&pThis->Allocation, (char *)pThis->pszUtf8);
+ RTAsn1ContentFree(&pThis->Asn1Core);
+ RT_ZERO(*pThis);
+ }
+}
+
+
+RTDECL(int) RTAsn1String_Enum(PRTASN1STRING pThis, PFNRTASN1ENUMCALLBACK pfnCallback, uint32_t uDepth, void *pvUser)
+{
+ RT_NOREF_PV(pThis); RT_NOREF_PV(pfnCallback); RT_NOREF_PV(uDepth); RT_NOREF_PV(pvUser);
+ Assert(pThis && (!RTAsn1String_IsPresent(pThis) || pThis->Asn1Core.pOps == &g_RTAsn1String_Vtable));
+
+ /* No children to enumerate. */
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1String_Compare(PCRTASN1STRING pLeft, PCRTASN1STRING pRight)
+{
+ /* Compare tag and binary value. */
+ return RTAsn1String_CompareEx(pLeft, pRight, true /*fTypeToo*/);
+}
+
+
+RTDECL(int) RTAsn1String_CheckSanity(PCRTASN1STRING pThis, uint32_t fFlags, PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ RT_NOREF_PV(fFlags);
+ if (RT_UNLIKELY(!RTAsn1String_IsPresent(pThis)))
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_NOT_PRESENT, "%s: Missing (STRING).", pszErrorTag);
+ return rtAsn1String_CheckSanity(pThis, pErrInfo, pszErrorTag, NULL /*pcchUtf8*/);
+}
+
+
+/*
+ * Generate code for the tag specific methods.
+ * Note! This is very similar to what we're doing in asn1-ut-time.cpp.
+ */
+#define RTASN1STRING_IMPL(a_uTag, a_szTag, a_Api) \
+ \
+ RTDECL(int) RT_CONCAT(a_Api,_Init)(PRTASN1STRING pThis, PCRTASN1ALLOCATORVTABLE pAllocator) \
+ { \
+ return RTAsn1String_InitEx(pThis, a_uTag, NULL /*pvValue*/, 0 /*cbValue*/, pAllocator); \
+ } \
+ \
+ RTDECL(int) RT_CONCAT(a_Api,_Clone)(PRTASN1STRING pThis, PCRTASN1STRING pSrc, PCRTASN1ALLOCATORVTABLE pAllocator) \
+ { \
+ AssertReturn(RTASN1CORE_GET_TAG(&pSrc->Asn1Core) == a_uTag || !RTAsn1String_IsPresent(pSrc), \
+ VERR_ASN1_STRING_TAG_MISMATCH); \
+ return RTAsn1String_Clone(pThis, pSrc, pAllocator); \
+ } \
+ \
+ RTDECL(void) RT_CONCAT(a_Api,_Delete)(PRTASN1STRING pThis) \
+ { \
+ Assert( !pThis \
+ || !RTAsn1String_IsPresent(pThis) \
+ || ( pThis->Asn1Core.pOps == &g_RTAsn1String_Vtable \
+ && RTASN1CORE_GET_TAG(&pThis->Asn1Core) == a_uTag) ); \
+ RTAsn1String_Delete(pThis); \
+ } \
+ \
+ RTDECL(int) RT_CONCAT(a_Api,_Enum)(PRTASN1STRING pThis, PFNRTASN1ENUMCALLBACK pfnCallback, uint32_t uDepth, void *pvUser) \
+ { \
+ RT_NOREF_PV(pThis); RT_NOREF_PV(pfnCallback); RT_NOREF_PV(uDepth); RT_NOREF_PV(pvUser); \
+ Assert( pThis \
+ && ( !RTAsn1String_IsPresent(pThis) \
+ || ( pThis->Asn1Core.pOps == &g_RTAsn1String_Vtable \
+ && RTASN1CORE_GET_TAG(&pThis->Asn1Core) == a_uTag) ) ); \
+ /* No children to enumerate. */ \
+ return VINF_SUCCESS; \
+ } \
+ \
+ RTDECL(int) RT_CONCAT(a_Api,_Compare)(PCRTASN1STRING pLeft, PCRTASN1STRING pRight) \
+ { \
+ int iDiff = RTAsn1String_CompareEx(pLeft, pRight, true /*fTypeToo*/); \
+ if (!iDiff && RTASN1CORE_GET_TAG(&pLeft->Asn1Core) != a_uTag && RTAsn1String_IsPresent(pLeft)) \
+ iDiff = RTASN1CORE_GET_TAG(&pLeft->Asn1Core) < a_uTag ? -1 : 1; \
+ return iDiff; \
+ } \
+ \
+ RTDECL(int) RT_CONCAT(a_Api,_CheckSanity)(PCRTASN1STRING pThis, uint32_t fFlags, \
+ PRTERRINFO pErrInfo, const char *pszErrorTag) \
+ { \
+ if (RTASN1CORE_GET_TAG(&pThis->Asn1Core) != a_uTag && RTAsn1String_IsPresent(pThis)) \
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_STRING_TAG_MISMATCH, "%s: uTag=%#x, expected %#x (%s)", \
+ pszErrorTag, RTASN1CORE_GET_TAG(&pThis->Asn1Core), a_uTag, a_szTag); \
+ return RTAsn1String_CheckSanity(pThis, fFlags, pErrInfo, pszErrorTag); \
+ }
+
+#include "asn1-ut-string-template2.h"
+
+
+/*
+ * Generate code for the associated collection types.
+ */
+#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-string-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+#include <iprt/asn1-generator-core.h>
+#include <iprt/asn1-generator-init.h>
+#include <iprt/asn1-generator-sanity.h>
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-time-decode.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-time-decode.cpp
new file mode 100644
index 00000000..5df4b73d
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-time-decode.cpp
@@ -0,0 +1,415 @@
+/* $Id: asn1-ut-time-decode.cpp $ */
+/** @file
+ * IPRT - ASN.1, UTC TIME and GENERALIZED TIME Types, Decoding.
+ */
+
+/*
+ * 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/err.h>
+#include <iprt/string.h>
+#include <iprt/ctype.h>
+
+#include <iprt/formats/asn1.h>
+
+
+/**
+ * Common code for UTCTime and GeneralizedTime converters that normalizes the
+ * converted time and checks that the input values doesn't change.
+ *
+ * @returns IPRT status code.
+ * @param pCursor The cursor to use when reporting an error.
+ * @param pThis The time to normalize and check.
+ * @param pszType The type name.
+ * @param pszErrorTag The error tag.
+ */
+static int rtAsn1Time_NormalizeTime(PRTASN1CURSOR pCursor, PRTASN1TIME pThis, const char *pszType, const char *pszErrorTag)
+{
+ int rc;
+ if ( pThis->Time.u8Month > 0
+ && pThis->Time.u8Month <= 12
+ && pThis->Time.u8Hour < 24
+ && pThis->Time.u8Minute < 60
+ && pThis->Time.u8Second <= 60)
+ {
+ /* Work around clever rounding error in DER_CFDateToUTCTime() on OS X. This also
+ supresses any attempt at feeding us leap seconds. If we pass 60 to the
+ normalization code will move on to the next min/hour/day, which is wrong both
+ for the OS X issue and for unwanted leap seconds. Leap seconds are not valid
+ ASN.1 by the by according to the specs available to us. */
+ if (pThis->Time.u8Second < 60)
+ { /* likely */ }
+ else
+ pThis->Time.u8Second = 59;
+
+ /* Normalize and move on. */
+ RTTIME const TimeCopy = pThis->Time;
+ if (RTTimeNormalize(&pThis->Time))
+ {
+ if ( TimeCopy.u8MonthDay == pThis->Time.u8MonthDay
+ && TimeCopy.u8Month == pThis->Time.u8Month
+ && TimeCopy.i32Year == pThis->Time.i32Year
+ && TimeCopy.u8Hour == pThis->Time.u8Hour
+ && TimeCopy.u8Minute == pThis->Time.u8Minute
+ && TimeCopy.u8Second == pThis->Time.u8Second)
+ return VINF_SUCCESS;
+
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_TIME_NORMALIZE_MISMATCH,
+ "%s: Normalized result not the same as %s: '%.*s' / %04u-%02u-%02uT%02u:%02u:%02u vs %04u-%02u-%02uT%02u:%02u:%02u",
+ pszErrorTag, pszType, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch,
+ TimeCopy.i32Year, TimeCopy.u8Month, TimeCopy.u8MonthDay,
+ TimeCopy.u8Hour, TimeCopy.u8Minute, TimeCopy.u8Second,
+ pThis->Time.i32Year, pThis->Time.u8Month, pThis->Time.u8MonthDay,
+ pThis->Time.u8Hour, pThis->Time.u8Minute, pThis->Time.u8Second);
+ }
+ else
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_TIME_NORMALIZE_ERROR,
+ "%s: RTTimeNormalize failed on %s: '%.*s'",
+ pszErrorTag, pszType, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
+ }
+ else
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_TIME_BAD_NORMALIZE_INPUT,
+ "%s: Bad %s values: '%.*s'; mth=%u h=%u min=%u sec=%u",
+ pszErrorTag, pszType, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch,
+ pThis->Time.u8Month, pThis->Time.u8Hour, pThis->Time.u8Minute, pThis->Time.u8Second);
+ return rc;
+}
+
+
+/**
+ * Converts the UTCTime string into an the RTTIME member of RTASN1TIME.
+ *
+ * @returns IPRT status code.
+ * @param pCursor The cursor to use when reporting an error.
+ * @param pThis The time to parse.
+ * @param pszErrorTag The error tag.
+ */
+static int rtAsn1Time_ConvertUTCTime(PRTASN1CURSOR pCursor, PRTASN1TIME pThis, const char *pszErrorTag)
+{
+ /*
+ * While the current spec says the seconds field is not optional, this
+ * restriction was added later on. So, when parsing UTCTime we must deal
+ * with it being absent.
+ */
+ int rc;
+ bool fHaveSeconds = pThis->Asn1Core.cb == sizeof("YYMMDDHHMMSSZ") - 1;
+ if (fHaveSeconds || pThis->Asn1Core.cb == sizeof("YYMMDDHHMMZ") - 1)
+ {
+ const char *pachTime = pThis->Asn1Core.uData.pch;
+
+ /* Basic encoding validation. */
+ if ( RT_C_IS_DIGIT(pachTime[0]) /* Y */
+ && RT_C_IS_DIGIT(pachTime[1]) /* Y */
+ && RT_C_IS_DIGIT(pachTime[2]) /* M */
+ && RT_C_IS_DIGIT(pachTime[3]) /* M */
+ && RT_C_IS_DIGIT(pachTime[4]) /* D */
+ && RT_C_IS_DIGIT(pachTime[5]) /* D */
+ && RT_C_IS_DIGIT(pachTime[6]) /* H */
+ && RT_C_IS_DIGIT(pachTime[7]) /* H */
+ && RT_C_IS_DIGIT(pachTime[8]) /* M */
+ && RT_C_IS_DIGIT(pachTime[9]) /* M */
+ && ( !fHaveSeconds
+ || ( RT_C_IS_DIGIT(pachTime[10]) /* S */
+ && RT_C_IS_DIGIT(pachTime[11]) /* S */ ) )
+ && pachTime[fHaveSeconds ? 12 : 10] == 'Z'
+ )
+ {
+ /* Basic conversion. */
+ pThis->Time.i32Year = (pachTime[0] - '0') * 10 + (pachTime[1] - '0');
+ pThis->Time.i32Year += pThis->Time.i32Year < 50 ? 2000 : 1900;
+ pThis->Time.u8Month = (pachTime[2] - '0') * 10 + (pachTime[3] - '0');
+ pThis->Time.u8WeekDay = 0;
+ pThis->Time.u16YearDay = 0;
+ pThis->Time.u8MonthDay = (pachTime[4] - '0') * 10 + (pachTime[5] - '0');
+ pThis->Time.u8Hour = (pachTime[6] - '0') * 10 + (pachTime[7] - '0');
+ pThis->Time.u8Minute = (pachTime[8] - '0') * 10 + (pachTime[9] - '0');
+ if (fHaveSeconds)
+ pThis->Time.u8Second = (pachTime[10] - '0') * 10 + (pachTime[11] - '0');
+ else
+ pThis->Time.u8Second = 0;
+ pThis->Time.u32Nanosecond = 0;
+ pThis->Time.fFlags = RTTIME_FLAGS_TYPE_UTC;
+ pThis->Time.offUTC = 0;
+
+ /* Check the convered data and normalize the time structure. */
+ rc = rtAsn1Time_NormalizeTime(pCursor, pThis, "UTCTime", pszErrorTag);
+ if (RT_SUCCESS(rc))
+ return rc;
+ }
+ else
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_UTC_TIME_ENCODING, "%s: Bad UTCTime encoding: '%.*s'",
+ pszErrorTag, pThis->Asn1Core.cb, pachTime);
+ }
+ else
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_UTC_TIME_ENCODING, "%s: Bad UTCTime length: %#x",
+ pszErrorTag, pThis->Asn1Core.cb);
+ RT_ZERO(*pThis);
+ return rc;
+}
+
+
+/**
+ * Converts the fraction part of a generalized time into nanoseconds.
+ *
+ * @returns IPRT status code.
+ * @param pCursor The cursor to use when reporting an error.
+ * @param pchFraction Pointer to the start of the fraction (dot).
+ * @param cchFraction The length of the fraction.
+ * @param pThis The time object we're working on,
+ * Time.u32Nanoseconds will be update.
+ * @param pszErrorTag The error tag.
+ */
+static int rtAsn1Time_ConvertGeneralizedTimeFraction(PRTASN1CURSOR pCursor, const char *pchFraction, uint32_t cchFraction,
+ PRTASN1TIME pThis, const char *pszErrorTag)
+{
+ pThis->Time.u32Nanosecond = 0;
+
+ /*
+ * Check the dot.
+ */
+ if (*pchFraction != '.')
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING,
+ "%s: Expected GeneralizedTime fraction dot, found: '%c' ('%.*s')",
+ pszErrorTag, *pchFraction, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
+ pchFraction++;
+ cchFraction--;
+ if (!cchFraction)
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING,
+ "%s: No digit following GeneralizedTime fraction dot: '%.*s'",
+ pszErrorTag, pThis->Asn1Core.cb, pThis->Asn1Core);
+
+ /*
+ * Do the conversion.
+ */
+ char chLastDigit;
+ uint32_t uMult = 100000000;
+ do
+ {
+ char chDigit = chLastDigit = *pchFraction;
+ if (!RT_C_IS_DIGIT(chDigit))
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING,
+ "%s: Bad GeneralizedTime fraction digit: '%.*s'",
+ pszErrorTag, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
+ pThis->Time.u32Nanosecond += uMult * (uint32_t)(chDigit - '0');
+
+ /* Advance */
+ cchFraction--;
+ pchFraction++;
+ uMult /= 10;
+ } while (cchFraction > 0 && uMult > 0);
+
+ /*
+ * Lazy bird: For now, we don't permit higher resolution than we can
+ * internally represent. Deal with this if it ever becomes an issue.
+ */
+ if (cchFraction > 0)
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING,
+ "%s: Bad GeneralizedTime fraction too long: '%.*s'",
+ pszErrorTag, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
+ if (chLastDigit == '0')
+ return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING,
+ "%s: Trailing zeros not allowed for GeneralizedTime: '%.*s'",
+ pszErrorTag, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Converts the GeneralizedTime string into an the RTTIME member of RTASN1TIME.
+ *
+ * @returns IPRT status code.
+ * @param pCursor The cursor to use when reporting an error.
+ * @param pThis The time to parse.
+ * @param pszErrorTag The error tag.
+ */
+static int rtAsn1Time_ConvertGeneralizedTime(PRTASN1CURSOR pCursor, PRTASN1TIME pThis, const char *pszErrorTag)
+{
+ int rc;
+ if (pThis->Asn1Core.cb >= sizeof("YYYYMMDDHHMMSSZ") - 1)
+ {
+ const char *pachTime = pThis->Asn1Core.uData.pch;
+
+ /* Basic encoding validation. */
+ if ( RT_C_IS_DIGIT(pachTime[0]) /* Y */
+ && RT_C_IS_DIGIT(pachTime[1]) /* Y */
+ && RT_C_IS_DIGIT(pachTime[2]) /* Y */
+ && RT_C_IS_DIGIT(pachTime[3]) /* Y */
+ && RT_C_IS_DIGIT(pachTime[4]) /* M */
+ && RT_C_IS_DIGIT(pachTime[5]) /* M */
+ && RT_C_IS_DIGIT(pachTime[6]) /* D */
+ && RT_C_IS_DIGIT(pachTime[7]) /* D */
+ && RT_C_IS_DIGIT(pachTime[8]) /* H */
+ && RT_C_IS_DIGIT(pachTime[9]) /* H */
+ && RT_C_IS_DIGIT(pachTime[10]) /* M */
+ && RT_C_IS_DIGIT(pachTime[11]) /* M */
+ && RT_C_IS_DIGIT(pachTime[12]) /* S */ /** @todo was this once optional? */
+ && RT_C_IS_DIGIT(pachTime[13]) /* S */
+ && pachTime[pThis->Asn1Core.cb - 1] == 'Z'
+ )
+ {
+ /* Basic conversion. */
+ pThis->Time.i32Year = 1000 * (pachTime[0] - '0')
+ + 100 * (pachTime[1] - '0')
+ + 10 * (pachTime[2] - '0')
+ + (pachTime[3] - '0');
+ pThis->Time.u8Month = (pachTime[4] - '0') * 10 + (pachTime[5] - '0');
+ pThis->Time.u8WeekDay = 0;
+ pThis->Time.u16YearDay = 0;
+ pThis->Time.u8MonthDay = (pachTime[6] - '0') * 10 + (pachTime[7] - '0');
+ pThis->Time.u8Hour = (pachTime[8] - '0') * 10 + (pachTime[9] - '0');
+ pThis->Time.u8Minute = (pachTime[10] - '0') * 10 + (pachTime[11] - '0');
+ pThis->Time.u8Second = (pachTime[12] - '0') * 10 + (pachTime[13] - '0');
+ pThis->Time.u32Nanosecond = 0;
+ pThis->Time.fFlags = RTTIME_FLAGS_TYPE_UTC;
+ pThis->Time.offUTC = 0;
+
+ /* Optional fraction part. */
+ rc = VINF_SUCCESS;
+ uint32_t cchLeft = pThis->Asn1Core.cb - 14 - 1;
+ if (cchLeft > 0)
+ rc = rtAsn1Time_ConvertGeneralizedTimeFraction(pCursor, pachTime + 14, cchLeft, pThis, pszErrorTag);
+
+ /* Check the convered data and normalize the time structure. */
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtAsn1Time_NormalizeTime(pCursor, pThis, "GeneralizedTime", pszErrorTag);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ }
+ }
+ else
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING,
+ "%s: Bad GeneralizedTime encoding: '%.*s'",
+ pszErrorTag, pThis->Asn1Core.cb, pachTime);
+ }
+ else
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING,
+ "%s: Bad GeneralizedTime length: %#x",
+ pszErrorTag, pThis->Asn1Core.cb);
+ RT_ZERO(*pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1Time_DecodeAsn1(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1TIME pThis, const char *pszErrorTag)
+{
+ Assert(!(fFlags & RTASN1CURSOR_GET_F_IMPLICIT)); RT_NOREF_PV(fFlags);
+ int rc = RTAsn1CursorReadHdr(pCursor, &pThis->Asn1Core, pszErrorTag);
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->Asn1Core.fClass == (ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE) )
+ {
+ if (pThis->Asn1Core.uTag == ASN1_TAG_UTC_TIME)
+ {
+ RTAsn1CursorSkip(pCursor, pThis->Asn1Core.cb);
+ pThis->Asn1Core.pOps = &g_RTAsn1Time_Vtable;
+ pThis->Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
+ return rtAsn1Time_ConvertUTCTime(pCursor, pThis, pszErrorTag);
+ }
+
+ if (pThis->Asn1Core.uTag == ASN1_TAG_GENERALIZED_TIME)
+ {
+ RTAsn1CursorSkip(pCursor, pThis->Asn1Core.cb);
+ pThis->Asn1Core.pOps = &g_RTAsn1Time_Vtable;
+ pThis->Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
+ return rtAsn1Time_ConvertGeneralizedTime(pCursor, pThis, pszErrorTag);
+ }
+
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_TAG_MISMATCH, "%s: Not UTCTime nor GeneralizedTime: uTag=%#x",
+ pszErrorTag, pThis->Asn1Core.uTag);
+ }
+ else
+ rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_TAG_FLAG_CLASS_MISMATCH,
+ "%s: Not UTCTime nor GeneralizedTime: fClass=%#x / uTag=%#x",
+ pszErrorTag, pThis->Asn1Core.fClass, pThis->Asn1Core.uTag);
+ }
+ RT_ZERO(*pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1UtcTime_DecodeAsn1(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1TIME pThis, const char *pszErrorTag)
+{
+ int rc = RTAsn1CursorReadHdr(pCursor, &pThis->Asn1Core, pszErrorTag);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTAsn1CursorMatchTagClassFlags(pCursor, &pThis->Asn1Core, ASN1_TAG_UTC_TIME,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ fFlags, pszErrorTag, "UTC TIME");
+ if (RT_SUCCESS(rc))
+ {
+ RTAsn1CursorSkip(pCursor, pThis->Asn1Core.cb);
+ pThis->Asn1Core.pOps = &g_RTAsn1Time_Vtable;
+ pThis->Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
+ return rtAsn1Time_ConvertUTCTime(pCursor, pThis, pszErrorTag);
+ }
+ }
+ RT_ZERO(*pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1GeneralizedTime_DecodeAsn1(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1TIME pThis, const char *pszErrorTag)
+{
+ int rc = RTAsn1CursorReadHdr(pCursor, &pThis->Asn1Core, pszErrorTag);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTAsn1CursorMatchTagClassFlags(pCursor, &pThis->Asn1Core, ASN1_TAG_GENERALIZED_TIME,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ fFlags, pszErrorTag, "GENERALIZED TIME");
+ if (RT_SUCCESS(rc))
+ {
+ RTAsn1CursorSkip(pCursor, pThis->Asn1Core.cb);
+ pThis->Asn1Core.pOps = &g_RTAsn1Time_Vtable;
+ pThis->Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
+ return rtAsn1Time_ConvertGeneralizedTime(pCursor, pThis, pszErrorTag);
+ }
+ }
+ RT_ZERO(*pThis);
+ return rc;
+}
+
+
+/*
+ * Generate code for the associated collection types.
+ */
+#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-time-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+#include <iprt/asn1-generator-asn1-decoder.h>
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-time-template.h b/src/VBox/Runtime/common/asn1/asn1-ut-time-template.h
new file mode 100644
index 00000000..06ba84aa
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-time-template.h
@@ -0,0 +1,55 @@
+/* $Id: asn1-ut-time-template.h $ */
+/** @file
+ * IPRT - ASN.1, UTC TIME and GENERALIZED TIME Types, Collection Type Template.
+ */
+
+/*
+ * 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
+ */
+
+#define RTASN1TMPL_DECL RTDECL
+
+#define RTASN1TMPL_TYPE RTASN1SEQOFTIMES
+#define RTASN1TMPL_EXT_NAME RTAsn1SeqOfTimes
+#define RTASN1TMPL_INT_NAME rtAsn1SeqOfTimes
+RTASN1TMPL_SEQ_OF(RTASN1TIME, RTAsn1Time);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
+
+#define RTASN1TMPL_TYPE RTASN1SETOFTIMES
+#define RTASN1TMPL_EXT_NAME RTAsn1SetOfTimes
+#define RTASN1TMPL_INT_NAME rtAsn1SetOfTimes
+RTASN1TMPL_SET_OF(RTASN1TIME, RTAsn1Time);
+#undef RTASN1TMPL_TYPE
+#undef RTASN1TMPL_EXT_NAME
+#undef RTASN1TMPL_INT_NAME
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-time-template2.h b/src/VBox/Runtime/common/asn1/asn1-ut-time-template2.h
new file mode 100644
index 00000000..1c341bdc
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-time-template2.h
@@ -0,0 +1,40 @@
+/* $Id: asn1-ut-time-template2.h $ */
+/** @file
+ * IPRT - ASN.1, UTC TIME and GENERALIZED TIME Types, Template for type specific wrappers.
+ */
+
+/*
+ * 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
+ */
+
+
+RTASN1TIME_IMPL(ASN1_TAG_UTC_TIME, "UTC TIME", RTAsn1UtcTime)
+RTASN1TIME_IMPL(ASN1_TAG_GENERALIZED_TIME, "GENERALIZED TIME", RTAsn1GeneralizedTime)
+
diff --git a/src/VBox/Runtime/common/asn1/asn1-ut-time.cpp b/src/VBox/Runtime/common/asn1/asn1-ut-time.cpp
new file mode 100644
index 00000000..d7176e91
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/asn1-ut-time.cpp
@@ -0,0 +1,359 @@
+/* $Id: asn1-ut-time.cpp $ */
+/** @file
+ * IPRT - ASN.1, UTC TIME and GENERALIZED TIME Types.
+ */
+
+/*
+ * 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/ctype.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/uni.h>
+
+#include <iprt/formats/asn1.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The UTC TIME encoding of the IPRT epoch time. */
+static const char g_szEpochUtc[] = "700101000000Z";
+/** The GENERALIZED TIME encoding of the IPRT epoch time. */
+static const char g_szEpochGeneralized[] = "19700101000000Z";
+
+
+/*
+ * ASN.1 TIME - Special Methods.
+ */
+
+RTDECL(int) RTAsn1Time_InitEx(PRTASN1TIME pThis, uint32_t uTag, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ RT_NOREF_PV(pAllocator);
+ AssertReturn(uTag == ASN1_TAG_UTC_TIME || uTag == ASN1_TAG_GENERALIZED_TIME, VERR_INVALID_PARAMETER);
+ RTAsn1Core_InitEx(&pThis->Asn1Core,
+ uTag,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ &g_RTAsn1Time_Vtable,
+ RTASN1CORE_F_PRESENT | RTASN1CORE_F_PRIMITE_TAG_STRUCT);
+ if (uTag == ASN1_TAG_UTC_TIME)
+ {
+ pThis->Asn1Core.cb = sizeof(g_szEpochUtc) - 1;
+ pThis->Asn1Core.uData.pv = &g_szEpochUtc[0];
+ }
+ else
+ {
+ pThis->Asn1Core.cb = sizeof(g_szEpochGeneralized) - 1;
+ pThis->Asn1Core.uData.pv = &g_szEpochGeneralized[0];
+ }
+
+ RTTIMESPEC EpochTimeSpec;
+ RTTimeExplode(&pThis->Time, RTTimeSpecSetSeconds(&EpochTimeSpec, 0));
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1Time_InitWithTime(PRTASN1TIME pThis, uint32_t uTag, PCRTASN1ALLOCATORVTABLE pAllocator, PCRTTIME pTime)
+{
+ int rc = RTAsn1Time_InitEx(pThis, uTag, pAllocator); /* this doens't leave any state needing deletion */
+ if (RT_SUCCESS(rc) && pTime)
+ rc = RTAsn1Time_SetTime(pThis, pAllocator, pTime);
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1Time_SetTime(PRTASN1TIME pThis, PCRTASN1ALLOCATORVTABLE pAllocator, PCRTTIME pTime)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(RTAsn1Time_IsPresent(pThis), VERR_INVALID_STATE); /* Use RTAsn1Time_InitWithTime. */
+
+ RTTIMESPEC TmpTimeSpec;
+ AssertReturn(RTTimeImplode(&TmpTimeSpec, pTime), VERR_INVALID_PARAMETER);
+ RTTIME NormalizedTime;
+ RTTimeExplode(&NormalizedTime, &TmpTimeSpec);
+
+ uint32_t const uTag = RTASN1CORE_GET_TAG(&pThis->Asn1Core);
+ if (uTag == ASN1_TAG_UTC_TIME)
+ {
+ AssertReturn(NormalizedTime.i32Year >= 1950, VERR_INVALID_PARAMETER);
+ AssertReturn(NormalizedTime.i32Year < 2050, VERR_INVALID_PARAMETER);
+ }
+ else
+ {
+ AssertReturn(uTag == ASN1_TAG_GENERAL_STRING, VERR_INVALID_STATE);
+ AssertReturn(NormalizedTime.i32Year >= 0, VERR_INVALID_PARAMETER);
+ AssertReturn(NormalizedTime.i32Year < 9999, VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Format the string to a temporary buffer, since the ASN.1 content isn't
+ * zero terminated and we cannot use RTStrPrintf directly on it.
+ */
+ char szTmp[64];
+ uint32_t cchTime;
+ if (uTag == ASN1_TAG_UTC_TIME)
+ cchTime = (uint32_t)RTStrPrintf(szTmp, sizeof(szTmp), "%02u%02u%02u%02u%02u%02uZ",
+ NormalizedTime.i32Year % 100,
+ NormalizedTime.u8Month,
+ NormalizedTime.u8MonthDay,
+ NormalizedTime.u8Hour,
+ NormalizedTime.u8Minute,
+ NormalizedTime.u8Second);
+ else
+ cchTime = (uint32_t)RTStrPrintf(szTmp, sizeof(szTmp), "%04u%02u%02u%02u%02u%02uZ",
+ NormalizedTime.i32Year,
+ NormalizedTime.u8Month,
+ NormalizedTime.u8MonthDay,
+ NormalizedTime.u8Hour,
+ NormalizedTime.u8Minute,
+ NormalizedTime.u8Second);
+ AssertReturn(cchTime == (uTag == ASN1_TAG_UTC_TIME ? sizeof(g_szEpochUtc) - 1 : sizeof(g_szEpochGeneralized) - 1),
+ VERR_INTERNAL_ERROR_3);
+
+ /*
+ * (Re-)Allocate content buffer, copy over the formatted timestamp and
+ * set the exploded time member to the new time.
+ */
+ int rc = RTAsn1ContentReallocZ(&pThis->Asn1Core, cchTime, pAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ memcpy((void *)pThis->Asn1Core.uData.pv, szTmp, cchTime);
+ pThis->Time = NormalizedTime;
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTAsn1Time_SetTimeSpec(PRTASN1TIME pThis, PCRTASN1ALLOCATORVTABLE pAllocator, PCRTTIMESPEC pTimeSpec)
+{
+ RTTIME Time;
+ return RTAsn1Time_SetTime(pThis, pAllocator, RTTimeExplode(&Time, pTimeSpec));
+}
+
+
+RTDECL(int) RTAsn1Time_CompareWithTimeSpec(PCRTASN1TIME pLeft, PCRTTIMESPEC pTsRight)
+{
+ int iDiff = RTAsn1Time_IsPresent(pLeft) ? 0 : -1;
+ if (!iDiff)
+ {
+ RTTIME RightTime;
+ iDiff = RTTimeCompare(&pLeft->Time, RTTimeExplode(&RightTime, pTsRight));
+ }
+
+ return iDiff;
+}
+
+
+/*
+ * ASN.1 TIME - Standard Methods.
+ */
+
+RT_DECL_DATA_CONST(RTASN1COREVTABLE const) g_RTAsn1Time_Vtable =
+{
+ "RTAsn1Time",
+ sizeof(RTASN1TIME),
+ UINT8_MAX,
+ ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
+ 0,
+ (PFNRTASN1COREVTDTOR)RTAsn1Time_Delete,
+ NULL,
+ (PFNRTASN1COREVTCLONE)RTAsn1Time_Clone,
+ (PFNRTASN1COREVTCOMPARE)RTAsn1Time_Compare,
+ (PFNRTASN1COREVTCHECKSANITY)RTAsn1Time_CheckSanity,
+ NULL,
+ NULL
+};
+
+
+RTDECL(int) RTAsn1Time_Init(PRTASN1TIME pThis, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ /* Using UTC TIME since epoch would be encoded using UTC TIME following
+ X.509 Validity / Whatever time tag guidelines. */
+ return RTAsn1Time_InitEx(pThis, ASN1_TAG_UTC_TIME, pAllocator);
+}
+
+
+RTDECL(int) RTAsn1Time_Clone(PRTASN1TIME pThis, PCRTASN1TIME pSrc, PCRTASN1ALLOCATORVTABLE pAllocator)
+{
+ AssertPtr(pSrc); AssertPtr(pThis); AssertPtr(pAllocator);
+ RT_ZERO(*pThis);
+ if (RTAsn1Time_IsPresent(pSrc))
+ {
+ AssertReturn(pSrc->Asn1Core.pOps == &g_RTAsn1Time_Vtable, VERR_INTERNAL_ERROR_3);
+
+ int rc = RTAsn1Core_CloneContent(&pThis->Asn1Core, &pSrc->Asn1Core, pAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->Time = pSrc->Time;
+ return VINF_SUCCESS;
+ }
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(void) RTAsn1Time_Delete(PRTASN1TIME pThis)
+{
+ if ( pThis
+ && RTAsn1Time_IsPresent(pThis))
+ {
+ Assert(pThis->Asn1Core.pOps == &g_RTAsn1Time_Vtable);
+
+ RTAsn1ContentFree(&pThis->Asn1Core);
+ RT_ZERO(*pThis);
+ }
+}
+
+
+RTDECL(int) RTAsn1Time_Enum(PRTASN1TIME pThis, PFNRTASN1ENUMCALLBACK pfnCallback, uint32_t uDepth, void *pvUser)
+{
+ RT_NOREF_PV(pThis); RT_NOREF_PV(pfnCallback); RT_NOREF_PV(uDepth); RT_NOREF_PV(pvUser);
+ Assert(pThis && (!RTAsn1Time_IsPresent(pThis) || pThis->Asn1Core.pOps == &g_RTAsn1Time_Vtable));
+
+ /* No children to enumerate. */
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTAsn1Time_Compare(PCRTASN1TIME pLeft, PCRTASN1TIME pRight)
+{
+ Assert(pLeft && (!RTAsn1Time_IsPresent(pLeft) || pLeft->Asn1Core.pOps == &g_RTAsn1Time_Vtable));
+ Assert(pRight && (!RTAsn1Time_IsPresent(pRight) || pRight->Asn1Core.pOps == &g_RTAsn1Time_Vtable));
+
+ int iDiff;
+ if (RTAsn1Time_IsPresent(pLeft))
+ {
+ if (RTAsn1Time_IsPresent(pRight))
+ iDiff = RTTimeCompare(&pLeft->Time, &pRight->Time);
+ else
+ iDiff = -1;
+ }
+ else
+ iDiff = 0 - (int)RTAsn1Time_IsPresent(pRight);
+ return iDiff;
+}
+
+
+RTDECL(int) RTAsn1Time_CheckSanity(PCRTASN1TIME pThis, uint32_t fFlags, PRTERRINFO pErrInfo, const char *pszErrorTag)
+{
+ RT_NOREF_PV(fFlags);
+ if (RT_UNLIKELY(!RTAsn1Time_IsPresent(pThis)))
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_NOT_PRESENT, "%s: Missing (TIME).", pszErrorTag);
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * Generate code for the tag specific methods.
+ * Note! This is very similar to what we're doing in asn1-ut-string.cpp.
+ */
+#define RTASN1TIME_IMPL(a_uTag, a_szTag, a_Api) \
+ \
+ RTDECL(int) RT_CONCAT(a_Api,_Init)(PRTASN1TIME pThis, PCRTASN1ALLOCATORVTABLE pAllocator) \
+ { \
+ return RTAsn1Time_InitEx(pThis, a_uTag, pAllocator); \
+ } \
+ \
+ RTDECL(int) RT_CONCAT(a_Api,_Clone)(PRTASN1TIME pThis, PCRTASN1TIME pSrc, PCRTASN1ALLOCATORVTABLE pAllocator) \
+ { \
+ AssertReturn(RTASN1CORE_GET_TAG(&pSrc->Asn1Core) == a_uTag || !RTAsn1Time_IsPresent(pSrc), \
+ VERR_ASN1_TIME_TAG_MISMATCH); \
+ return RTAsn1Time_Clone(pThis, pSrc, pAllocator); \
+ } \
+ \
+ RTDECL(void) RT_CONCAT(a_Api,_Delete)(PRTASN1TIME pThis) \
+ { \
+ Assert( !pThis \
+ || !RTAsn1Time_IsPresent(pThis) \
+ || ( pThis->Asn1Core.pOps == &g_RTAsn1Time_Vtable \
+ && RTASN1CORE_GET_TAG(&pThis->Asn1Core) == a_uTag) ); \
+ RTAsn1Time_Delete(pThis); \
+ } \
+ \
+ RTDECL(int) RT_CONCAT(a_Api,_Enum)(PRTASN1TIME pThis, PFNRTASN1ENUMCALLBACK pfnCallback, uint32_t uDepth, void *pvUser) \
+ { \
+ RT_NOREF_PV(pThis); RT_NOREF_PV(pfnCallback); RT_NOREF_PV(uDepth); RT_NOREF_PV(pvUser); \
+ Assert( pThis \
+ && ( !RTAsn1Time_IsPresent(pThis) \
+ || ( pThis->Asn1Core.pOps == &g_RTAsn1Time_Vtable \
+ && RTASN1CORE_GET_TAG(&pThis->Asn1Core) == a_uTag) ) ); \
+ /* No children to enumerate. */ \
+ return VINF_SUCCESS; \
+ } \
+ \
+ RTDECL(int) RT_CONCAT(a_Api,_Compare)(PCRTASN1TIME pLeft, PCRTASN1TIME pRight) \
+ { \
+ int iDiff = RTAsn1Time_Compare(pLeft, pRight); \
+ if (!iDiff && RTAsn1Time_IsPresent(pLeft)) \
+ { \
+ if (RTASN1CORE_GET_TAG(&pLeft->Asn1Core) == RTASN1CORE_GET_TAG(&pRight->Asn1Core)) \
+ { \
+ if (RTASN1CORE_GET_TAG(&pLeft->Asn1Core) != a_uTag) \
+ iDiff = RTASN1CORE_GET_TAG(&pLeft->Asn1Core) < a_uTag ? -1 : 1; \
+ } \
+ else \
+ iDiff = RTASN1CORE_GET_TAG(&pLeft->Asn1Core) < RTASN1CORE_GET_TAG(&pRight->Asn1Core) ? -1 : 1; \
+ } \
+ return iDiff; \
+ } \
+ \
+ RTDECL(int) RT_CONCAT(a_Api,_CheckSanity)(PCRTASN1TIME pThis, uint32_t fFlags, \
+ PRTERRINFO pErrInfo, const char *pszErrorTag) \
+ { \
+ if (RTASN1CORE_GET_TAG(&pThis->Asn1Core) != a_uTag && RTAsn1Time_IsPresent(pThis)) \
+ return RTErrInfoSetF(pErrInfo, VERR_ASN1_TIME_TAG_MISMATCH, "%s: uTag=%#x, expected %#x (%s)", \
+ pszErrorTag, RTASN1CORE_GET_TAG(&pThis->Asn1Core), a_uTag, a_szTag); \
+ return RTAsn1Time_CheckSanity(pThis, fFlags, pErrInfo, pszErrorTag); \
+ }
+
+#include "asn1-ut-time-template2.h"
+
+
+/*
+ * Generate code for the associated collection types.
+ */
+#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-time-template.h"
+#include <iprt/asn1-generator-internal-header.h>
+#include <iprt/asn1-generator-core.h>
+#include <iprt/asn1-generator-init.h>
+#include <iprt/asn1-generator-sanity.h>
+
diff --git a/src/VBox/Runtime/common/asn1/oiddb.cfg b/src/VBox/Runtime/common/asn1/oiddb.cfg
new file mode 100644
index 00000000..000bc47d
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/oiddb.cfg
@@ -0,0 +1,351 @@
+# $Id: oiddb.cfg $
+## @file
+# OID names.
+#
+# This is processed by oid2c.cpp and turned into a database which
+# the ASN.1 dumper code is using.
+#
+# Two formats here:
+# 1. Simple:
+# <ODI> = <name>
+# 2. The dumpasn1.cfg one:
+# ODI = <ODI>
+# <other stuff> = <ignored>
+# Description = <Description>
+#
+# The file doesn't need to be sorted, but we keep it sorted to make it
+# simpler to insert new OIDs and spot bugs.
+#
+
+#
+# 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
+#
+
+
+1.2.840 = usa
+1.2.840.10045 = ansi-x962
+1.2.840.10045.2 = ansi-x962-keyType
+1.2.840.10045.2.1 = ansi-x962-ecPublicKey
+1.2.840.10045.4 = ansi-x962-signatures
+1.2.840.10045.4.1 = ecdsa-with-sha1
+1.2.840.10045.4.2 = ecdsa-with-recommended
+1.2.840.10045.4.3 = ecdsa-with-sha2
+1.2.840.10045.4.3.1 = ecdsa-with-sha224
+1.2.840.10045.4.3.2 = ecdsa-with-sha256
+1.2.840.10045.4.3.3 = ecdsa-with-sha384
+1.2.840.10045.4.3.4 = ecdsa-with-sha512
+1.2.840.113549 = rsadsi
+1.2.840.113549.1 = pkcs
+1.2.840.113549.1.1 = pkcs1
+1.2.840.113549.1.1.1 = pkcs1-RsaEncryption
+1.2.840.113549.1.1.2 = pkcs1-Md2WithRsaEncryption
+1.2.840.113549.1.1.3 = pkcs1-Md4WithRsaEncryption
+1.2.840.113549.1.1.4 = pkcs1-Md5WithRsaEncryption
+1.2.840.113549.1.1.5 = pkcs1-Sha1WithRsaEncryption
+1.2.840.113549.1.1.10 = pkcs1-RsaPss
+1.2.840.113549.1.1.11 = pkcs1-Sha256WithRsaEncryption
+1.2.840.113549.1.1.12 = pkcs1-Sha384WithRsaEncryption
+1.2.840.113549.1.1.13 = pkcs1-Sha512WithRsaEncryption
+1.2.840.113549.1.1.14 = pkcs1-Sha224WithRsaEncryption
+1.2.840.113549.1.1.15 = pkcs1-Sha512-224WithRsaEncryption
+1.2.840.113549.1.1.16 = pkcs1-Sha512-256WithRsaEncryption
+1.2.840.113549.1.7 = pkcs7
+1.2.840.113549.1.7.1 = pkcs7-data
+1.2.840.113549.1.7.2 = pkcs7-signedData
+1.2.840.113549.1.7.3 = pkcs7-envelopedData
+1.2.840.113549.1.7.4 = pkcs7-signedAndEnvelopedData
+1.2.840.113549.1.7.5 = pkcs7-digestedData
+1.2.840.113549.1.7.6 = pkcs7-encryptedData
+1.2.840.113549.1.9 = pkcs9
+1.2.840.113549.1.9.1 = pkcs9-EMailAddress
+1.2.840.113549.1.9.2 = pkcs9-UntrustedName
+1.2.840.113549.1.9.3 = pkcs9-ContentType
+1.2.840.113549.1.9.4 = pkcs9-MessageDigest
+1.2.840.113549.1.9.5 = pkcs9-SigningTime
+1.2.840.113549.1.9.6 = pkcs9-CounterSignature
+1.2.840.113549.1.9.7 = pkcs9-challengePassword
+1.2.840.113549.1.9.8 = pkcs9-UnstructuredAddress
+1.2.840.113549.1.9.9 = pkcs9-ExtendedCertificateAttributes
+1.2.840.113549.1.9.13 = pkcs9-SigningDescription
+1.2.840.113549.1.9.14 = pkcs9-ExtensionRequest
+1.2.840.113549.1.9.15 = pkcs9-SMimeCapabilities
+1.2.840.113549.1.9.16 = pkcs9-SMime
+1.2.840.113549.1.9.16.1 = pkcs9-SMime-ct
+1.2.840.113549.1.9.16.1.2 = pkcs9-ct-AuthData
+1.2.840.113549.1.9.16.1.4 = pkcs9-ct-TimestampTokenInfo
+1.2.840.113549.1.9.16.1.9 = pkcs9-ct-CompressedData
+1.2.840.113549.1.9.16.1.23 = pkcs9-ct-AuthEnvelopedData
+1.2.840.113549.1.9.16.1.31 = pkcs9-ct-TimestampedData
+1.2.840.113549.1.9.16.2 = pkcs9-SMime-id-aa
+1.2.840.113549.1.9.16.2.12 = pkcs9-id-aa-SigningCertificate
+1.2.840.113549.1.9.16.2.14 = pkcs9-id-aa-Attributes
+1.2.840.113549.1.9.16.2.47 = pkcs9-id-aa-signingCertificateV2
+1.2.840.113549.1.9.25 = pkcs9-SMime-at
+1.2.840.113549.1.9.25.1 = pkcs9-at-Pkcs15Token
+1.2.840.113549.1.9.25.2 = pkcs9-at-EncryptedPrivateKeyInfo
+1.2.840.113549.1.9.25.3 = pkcs9-at-RandomNonce
+1.2.840.113549.1.9.25.4 = pkcs9-at-SequenceNumber
+1.2.840.113549.1.9.25.5 = pkcs9-at-Pkcs7PDU
+1.2.840.113635.100.6.1.13 = apple-cs-ext-DevId-Application
+1.2.840.113635.100.6.1.14 = apple-cs-ext-DevId-Installer
+1.2.840.113635.100.6.1.18 = apple-cs-ext-DevId-KernelExt
+1.2.840.113635.100.5.1 = apple-cert-policy
+1.2.840.113635.100.4.13 = apple-eku-packageSign
+#1.2.840.113635.100.9.1 = apple-???
+1.3.6 = dod
+1.3.6.1 = dod-Internet
+1.3.6.1.4.1.311 = microsoft
+1.3.6.1.4.1.311.2 = ms-authenticode
+1.3.6.1.4.1.311.2.1 = ms-spc
+1.3.6.1.4.1.311.2.1.1 = Ms-??-2.1
+1.3.6.1.4.1.311.2.1.4 = Ms-SpcIndirectDataContext
+1.3.6.1.4.1.311.2.1.10 = Ms-SpcAgencyInfo
+1.3.6.1.4.1.311.2.1.11 = Ms-SpcStatementType
+1.3.6.1.4.1.311.2.1.12 = Ms-SpcOpusInfo
+1.3.6.1.4.1.311.2.1.14 = Ms-CertReqExtensions
+1.3.6.1.4.1.311.2.1.15 = Ms-SpcPeImageData
+1.3.6.1.4.1.311.2.1.18 = Ms-SpcRawFileData
+1.3.6.1.4.1.311.2.1.19 = Ms-SpcStructuredStorageData
+1.3.6.1.4.1.311.2.1.20 = Ms-SpcJavaClassDataType1
+1.3.6.1.4.1.311.2.1.21 = Ms-SpcIndividualCodeSigning
+1.3.6.1.4.1.311.2.1.22 = Ms-SpcCommericalSigning
+1.3.6.1.4.1.311.2.1.25 = Ms-SpcLinkType2-Aka-CabData
+1.3.6.1.4.1.311.2.1.26 = Ms-SpcMinimalCriterialInfo
+1.3.6.1.4.1.311.2.1.27 = Ms-SpcFinacialCriterialInfo
+1.3.6.1.4.1.311.2.1.28 = Ms-SpcLinkType3
+1.3.6.1.4.1.311.2.1.29 = Ms-SpcHashInfo
+1.3.6.1.4.1.311.2.1.30 = Ms-SpcSipInfo
+1.3.6.1.4.1.311.2.2 = Ms-Ctl-CAs
+1.3.6.1.4.1.311.2.2.1 = Ms-TrustedCodeSigningCAList
+1.3.6.1.4.1.311.2.2.2 = Ms-TrustedClientAuthCAList
+1.3.6.1.4.1.311.2.2.3 = Ms-TrustedServerAuthCAList
+1.3.6.1.4.1.311.2.3.1 = Ms-SpcPeImagePageHashesV1
+1.3.6.1.4.1.311.2.3.2 = Ms-SpcPeImagePageHashesV2
+1.3.6.1.4.1.311.2.4 = Ms-??-.2.4
+1.3.6.1.4.1.311.2.4.1 = Ms-SpcNestedSignature
+1.3.6.1.4.1.311.3 = ms-timestamping
+1.3.6.1.4.1.311.3.2.1 = Ms-SpcTimeStampRequest
+1.3.6.1.4.1.311.3.3.1 = Ms-CounterSign
+1.3.6.1.4.1.311.3.3.2 = Ms-?-3.2
+1.3.6.1.4.1.311.10.1 = Ms-CertTrustList
+1.3.6.1.4.1.311.10.1.1 = Ms-SortedCertTrustList
+1.3.6.1.4.1.311.10.2 = Ms-NextUpdateLocation
+1.3.6.1.4.1.311.10.3 = Ms-EKU
+1.3.6.1.4.1.311.10.3.1 = Ms-kp-CertTrustListSigning
+1.3.6.1.4.1.311.10.3.2 = Ms-kp-TimeStampSigning
+1.3.6.1.4.1.311.10.3.3 = Ms-ServerGatedCrypto
+1.3.6.1.4.1.311.10.3.3.1 = Ms-Serialized
+1.3.6.1.4.1.311.10.3.4 = Ms-EncryptedFileSystem
+1.3.6.1.4.1.311.10.3.4.1 = Ms-EncryptedFileSystem-Recovery
+1.3.6.1.4.1.311.10.3.5 = Ms-WhqlCrypto
+1.3.6.1.4.1.311.10.3.6 = Ms-Nt5Crypto
+1.3.6.1.4.1.311.10.3.7 = Ms-OemWhqlCrypto
+1.3.6.1.4.1.311.10.3.8 = Ms-EmbeddedNtCrypto
+1.3.6.1.4.1.311.10.3.9 = Ms-RootListSigner
+1.3.6.1.4.1.311.10.3.10 = Ms-QualifiedSubordination
+1.3.6.1.4.1.311.10.3.11 = Ms-KeyRecovery
+1.3.6.1.4.1.311.10.3.12 = Ms-DocumentSigning
+1.3.6.1.4.1.311.10.3.13 = Ms-LifetimeSigning
+1.3.6.1.4.1.311.10.5.1 = Ms-Drm
+1.3.6.1.4.1.311.10.5.2 = Ms-DrmIndividualization
+1.3.6.1.4.1.311.10.9.1 = Ms-CrossCertDistPoints
+1.3.6.1.4.1.311.12 = Ms-Catalog
+1.3.6.1.4.1.311.12.1 = Ms-Catalog-dot-1
+1.3.6.1.4.1.311.12.1.1 = Ms-CatalogList
+1.3.6.1.4.1.311.12.1.2 = Ms-CatalogListMember
+1.3.6.1.4.1.311.12.2 = Ms-Catalog-dot-2
+1.3.6.1.4.1.311.12.2.1 = Ms-CatNameValue
+1.3.6.1.4.1.311.12.2.2 = Ms-CatMemberInfo
+1.3.6.1.4.1.311.15 = Ms-Java
+1.3.6.1.4.1.311.16 = Ms-Outlook-Exchange
+1.3.6.1.4.1.311.17 = Ms-PKCS12-attribute
+1.3.6.1.4.1.311.17.1 = Ms-PKCS12-LocalMachineKeyset
+1.3.6.1.4.1.311.20.1 = Ms-AutoEnrollCtlUsage
+1.3.6.1.4.1.311.20.2 = Ms-EnrollCerttypeExtension
+1.3.6.1.4.1.311.21 = Ms-CertSrv-Infrastructure
+1.3.6.1.4.1.311.21.1 = Ms-CaKeyCertIndexPair
+1.3.6.1.4.1.311.21.2 = Ms-CertSrvPreviousCertHash
+1.3.6.1.4.1.311.21.3 = Ms-CrlVirtualBase
+1.3.6.1.4.1.311.21.4 = Ms-CrlNextPublish
+1.3.6.1.4.1.311.21.6 = Ms-KeyRecovery
+1.3.6.1.4.1.311.21.7 = Ms-CertificateTemplate
+1.3.6.1.4.1.311.21.9 = Ms-DummySigner
+1.3.6.1.4.1.311.40 = Ms-Fonts
+1.3.6.1.4.1.311.41 = Ms-License-and-Registration
+1.3.6.1.4.1.311.42 = Ms-CorpPKI
+1.3.6.1.4.1.311.60 = Ms-ev
+1.3.6.1.4.1.311.60.1 = Ms-ev-rootProgram
+1.3.6.1.4.1.311.60.1.1 = Ms-ev-rootProgramFlags
+1.3.6.1.4.1.311.60.2 = Ms-ev-2
+1.3.6.1.4.1.311.60.2.1 = Ms-ev-2-1
+1.3.6.1.4.1.311.60.2.1.1 = Ms-jurisdictionOfIncorporationLocalityName
+1.3.6.1.4.1.311.60.2.1.2 = Ms-jurisdictionOfIncorporationStateOrProvinceNm
+1.3.6.1.4.1.311.60.2.1.3 = Ms-jurisdictionOfIncorporationCountryName
+1.3.6.1.4.1.311.88 = Ms-CapiCom
+1.3.6.1.5 = security
+1.3.6.1.5.5 = security-Mechanisms
+1.3.6.1.5.5.7 = public-key-infrastructure
+1.3.6.1.5.5.7.1.1 = pkix-AuthorityInfoAccess
+1.3.6.1.5.5.7.1.12 = pkix-LogoType
+1.3.6.1.5.5.7.2.1 = id-qt-CPS
+1.3.6.1.5.5.7.2.2 = id-qt-UNotice
+1.3.6.1.5.5.7.2.3 = id-qt-TextNotice
+1.3.6.1.5.5.7.2.4 = id-qt-ACPS
+1.3.6.1.5.5.7.2.5 = id-qt-ACUNotice
+1.3.6.1.5.5.7.3.1 = id-kp-ServerAuth
+1.3.6.1.5.5.7.3.2 = id-kp-ClientAuth
+1.3.6.1.5.5.7.3.3 = id-kp-CodeSigning
+1.3.6.1.5.5.7.3.4 = id-kp-EmailProtection
+1.3.6.1.5.5.7.3.5 = id-kp-IPSecEndSystem
+1.3.6.1.5.5.7.3.6 = id-kp-IPSecTunnel
+1.3.6.1.5.5.7.3.7 = id-kp-IPSecUser
+1.3.6.1.5.5.7.3.8 = id-kp-Timestamping
+1.3.6.1.5.5.7.3.9 = id-kp-OCSPSigning
+1.3.6.1.5.5.7.3.10 = id-kp-DVCS
+1.3.6.1.5.5.7.3.11 = id-kp-SBGPCertAAServiceAuth
+1.3.6.1.5.5.7.3.13 = id-kp-EAPOverPPP
+1.3.6.1.5.5.7.3.14 = id-kp-EAPOverLAN
+1.3.14.3 = oiw-ssig
+1.3.14.3.2 = oiw-ssig-algorithms
+1.3.14.3.2.2 = oiw-ssig-md4WithRsa
+1.3.14.3.2.3 = oiw-ssig-md5WithRsa
+1.3.14.3.2.4 = oiw-ssig-md4WithRsaEncryption
+1.3.14.3.2.14 = oiw-ssig-mdc2WithRsaSignature
+1.3.14.3.2.15 = oiw-ssig-sha0WithRsaSignature
+1.3.14.3.2.18 = oiw-ssig-sha0
+1.3.14.3.2.19 = oiw-ssig-mdc2
+1.3.14.3.2.21 = oiw-ssig-dsaCommonWithSha0
+1.3.14.3.2.24 = oiw-ssig-md2WithRsaSignature
+1.3.14.3.2.25 = oiw-ssig-md5WithRsaSignature
+1.3.14.3.2.26 = oiw-ssig-sha1
+1.3.14.3.2.29 = oiw-ssig-sha1WithRsaEncryption
+2 = joint-iso/itu-t
+2.1 = asn.1
+2.5 = x500
+2.5.4 = x500-attribute-types
+2.5.4.3 = x500-CommonName
+2.5.4.6 = x500-CountryName
+2.5.4.7 = x500-LocalityName
+2.5.4.8 = x500-StatOrProvinceName
+2.5.4.10 = x500-OrganizationName
+2.5.4.11 = x500-OrganizationUnitName
+2.5.29 = id-ce
+2.5.29.1 = id-ce-AuthorityKeyIdentifier-Deprecated
+2.5.29.2 = id-ce-KeyAttributes-Deprecated
+2.5.29.3 = id-ce-CertificatePolicies-Deprecated
+2.5.29.4 = id-ce-KeyUsageRestriction-Deprecated
+2.5.29.7 = id-ce-SubjectAltName-Deprecated
+2.5.29.8 = id-ce-IssuerAltName-Deprecated
+2.5.29.14 = id-ce-SubjectKeyIdentifier
+2.5.29.15 = id-ce-KeyUsage
+2.5.29.16 = id-ce-PrivateKeyUsagePeriod
+2.5.29.17 = id-ce-SubjectAltName
+2.5.29.18 = id-ce-issuerAltName
+2.5.29.19 = id-ce-BasicConstraints
+2.5.29.25 = id-ce-CrlDistributionPoints
+2.5.29.29 = id-ce-CertificateIssuer
+2.5.29.30 = id-ce-NameConstraints
+2.5.29.31 = id-ce-CrlDistributionPoints
+2.5.29.32 = id-ce-CertificatePolicies
+2.5.29.32.0 = id-ce-cp-anyPolicy
+2.5.29.35 = id-ce-AuthorityKeyIdentifier
+2.5.29.36 = id-ce-PolicyConstraints
+2.5.29.37 = id-ce-ExtKeyUsage
+2.16 = by-country
+2.16.840 = usa
+2.16.840.1 = usa-company-arc
+2.16.840.1.101 = usa-goverment
+2.16.840.1.101.3 = usa-computer-security-register
+2.16.840.1.101.3.4 = nist-algorithms
+2.16.840.1.101.3.4.1 = nist-aes-algorithms
+2.16.840.1.101.3.4.2 = nist-hash-algorithms
+2.16.840.1.101.3.4.2.1 = nist-sha256
+2.16.840.1.101.3.4.2.2 = nist-sha384
+2.16.840.1.101.3.4.2.3 = nist-sha512
+2.16.840.1.101.3.4.2.4 = nist-sha224
+2.16.840.1.101.3.4.2.5 = nist-sha512-224
+2.16.840.1.101.3.4.2.6 = nist-sha512-256
+2.16.840.1.101.3.4.2.7 = nist-sha3-224
+2.16.840.1.101.3.4.2.8 = nist-sha3-256
+2.16.840.1.101.3.4.2.9 = nist-sha3-384
+2.16.840.1.101.3.4.2.10 = nist-sha3-512
+2.16.840.1.101.3.4.2.11 = nist-shake128
+2.16.840.1.101.3.4.2.12 = nist-shake256
+2.16.840.1.101.3.4.3 = nist-sigAlgs
+2.16.840.1.101.3.4.3.1 = nist-dsa-with-sha224
+2.16.840.1.101.3.4.3.2 = nist-dsa-with-sha256
+2.16.840.1.101.3.4.3.3 = nist-dsa-with-sha384
+2.16.840.1.101.3.4.3.4 = nist-dsa-with-sha512
+2.16.840.1.101.3.4.3.5 = nist-id-dsa-with-sha224
+2.16.840.1.101.3.4.3.6 = nist-id-dsa-with-sha256
+2.16.840.1.101.3.4.3.7 = nist-id-dsa-with-sha384
+2.16.840.1.101.3.4.3.8 = nist-id-dsa-with-sha512
+2.16.840.1.101.3.4.3.9 = nist-id-ecdsa-with-sha3-224
+2.16.840.1.101.3.4.3.10 = nist-id-ecdsa-with-sha3-256
+2.16.840.1.101.3.4.3.11 = nist-id-ecdsa-with-sha3-384
+2.16.840.1.101.3.4.3.12 = nist-id-ecdsa-with-sha3-512
+2.16.840.1.101.3.4.3.13 = nist-id-rsassa-pkcs1-v1-5-with-sha3-224
+2.16.840.1.101.3.4.3.14 = nist-id-rsassa-pkcs1-v1-5-with-sha3-256
+2.16.840.1.101.3.4.3.15 = nist-id-rsassa-pkcs1-v1-5-with-sha3-384
+2.16.840.1.101.3.4.3.16 = nist-id-rsassa-pkcs1-v1-5-with-sha3-512
+2.16.840.1.113730 = netscape
+2.16.840.1.113730.1.1 = netscape-cert-type
+2.16.840.1.113730.1.2 = netscape-base-url
+2.16.840.1.113730.1.3 = netscape-revocation-url
+2.16.840.1.113730.1.4 = netscape-ca-revocation-url
+2.16.840.1.113730.1.7 = netscape-cert-renewal-url
+2.16.840.1.113730.1.8 = netscape-ca-policy-url
+2.16.840.1.113730.1.9 = netscape-HomePage-url
+2.16.840.1.113730.1.10 = netscape-EntityLogo
+2.16.840.1.113730.1.11 = netscape-UserPicture
+2.16.840.1.113730.1.12 = netscape-ssl-server-name
+2.16.840.1.113730.1.13 = netscape-comment
+2.16.840.1.113730.4.1 = netscape-eku-serverGatedCrypto
+2.16.840.1.113733 = verisign
+2.16.840.1.113733.1 = verisign-pki
+2.16.840.1.113733.1.6 = verisign-pki-extensions
+2.16.840.1.113733.1.6.7 = verisign-pki-ext-RolloverID
+2.16.840.1.113733.1.7 = verisign-pki-policies
+2.16.840.1.113733.1.7.9 = verisign-pki-policy-9
+2.16.840.1.113733.1.7.21 = verisign-pki-policy-21
+2.16.840.1.113733.1.7.23 = verisign-pki-policy-vtn-cp
+2.16.840.1.113733.1.7.23.1 = verisign-pki-policy-vtn-cp-class1
+2.16.840.1.113733.1.7.23.2 = verisign-pki-policy-vtn-cp-class2
+2.16.840.1.113733.1.7.23.3 = verisign-pki-policy-vtn-cp-class3
+2.16.840.1.113733.1.7.23.4 = verisign-pki-policy-vtn-cp-class4
+2.16.840.1.113733.1.7.23.6 = verisign-pki-policy-vtn-cp-6
+2.16.840.1.113733.1.7.46 = verisign-pki-policy-cis
+2.16.840.1.113733.1.7.46.1 = verisign-pki-policy-cis-type1
+2.16.840.1.113733.1.7.46.2 = verisign-pki-policy-cis-type2
+2.16.840.1.113733.1.7.48 = verisign-pki-policy-thawte
+2.16.840.1.113733.1.7.48.1 = verisign-pki-policy-thawte-cps-1
+# check the following one...
+2.16.840.1.113733.1.8.1 = verisign-pki-eku-IssStrongCrypto
+
diff --git a/src/VBox/Runtime/common/asn1/oiddb2c.cpp b/src/VBox/Runtime/common/asn1/oiddb2c.cpp
new file mode 100644
index 00000000..4fcb92cf
--- /dev/null
+++ b/src/VBox/Runtime/common/asn1/oiddb2c.cpp
@@ -0,0 +1,635 @@
+/* $Id: oiddb2c.cpp $ */
+/** @file
+ * IPRT - OID text database to C converter.
+ *
+ * The output is used by asn1-dump.cpp.
+ */
+
+/*
+ * 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 <iprt/assert.h>
+#include <iprt/types.h>
+#include <iprt/ctype.h>
+#include <iprt/stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/*
+ * Include the string table code.
+ */
+#define BLDPROG_STRTAB_MAX_STRLEN 48
+#define BLDPROG_STRTAB_WITH_COMPRESSION
+#define BLDPROG_STRTAB_PURE_ASCII
+#define BLDPROG_STRTAB_WITH_CAMEL_WORDS
+#include <iprt/bldprog-strtab-template.cpp.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define OID2C_MAX_COMP_VALUE _1G
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Raw OID tree node.
+ *
+ * This is what we produce while loading OID input files.
+ */
+typedef struct RAWOIDNODE
+{
+ /** The component value. */
+ uint32_t uKey;
+ /** Number of children. */
+ uint32_t cChildren;
+ /** Pointer to the children pointers (sorted by key). */
+ struct RAWOIDNODE **papChildren;
+ /** Pointer to the parent. */
+ struct RAWOIDNODE *pParent;
+ /** The string table entry for this node. */
+ BLDPROGSTRING StrTabEntry;
+ /** The table index of the children. */
+ uint32_t idxChildren;
+ /** Set if we've got one or more children with large keys. */
+ bool fChildrenInBigTable;
+} RAWOIDNODE;
+/** Pointer to a raw OID node. */
+typedef RAWOIDNODE *PRAWOIDNODE;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** What to prefix errors with. */
+static const char *g_pszProgName = "oiddb2c";
+
+/** The OID tree. */
+static PRAWOIDNODE g_pOidRoot = NULL;
+/** Number of nodes in the OID tree. */
+static uint32_t g_cOidNodes = 0;
+/** Number of nodes in the OID tree that has strings (for the string table). */
+static uint32_t g_cOidNodesWithStrings = 0;
+/** Max number of children of a node in the OID tree. */
+static uint32_t g_cMaxOidChildren = 0;
+/** Number of nodes which key fits within 6-bits. */
+static uint32_t g_cOidNodiesWith6bitKeys = 0;
+
+
+static RTEXITCODE error(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ fprintf(stderr, "%s: error: ", g_pszProgName);
+ vfprintf(stderr, pszFormat, va);
+ va_end(va);
+ return RTEXITCODE_FAILURE;
+}
+
+static RTEXITCODE warning(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ fprintf(stderr, "%s: warning: ", g_pszProgName);
+ vfprintf(stderr, pszFormat, va);
+ va_end(va);
+ return RTEXITCODE_FAILURE;
+}
+
+
+static void writeDottedOidForNode(PRAWOIDNODE pCurNode, FILE *pOut)
+{
+ if (pCurNode->pParent)
+ {
+ writeDottedOidForNode(pCurNode->pParent, pOut);
+ fprintf(pOut, ".%u", pCurNode->uKey);
+ }
+ else
+ fprintf(pOut, "%u", pCurNode->uKey);
+}
+
+
+static void writeOidTree(PRAWOIDNODE pCurNode, FILE *pOut, bool fBigTable, PBLDPROGSTRTAB pStrTab)
+{
+ /*
+ * First we produce the entries for our children.
+ */
+ if (pCurNode->fChildrenInBigTable == fBigTable)
+ {
+ for (unsigned i = 0; i < pCurNode->cChildren; i++)
+ {
+ PRAWOIDNODE pChild = pCurNode->papChildren[i];
+ fprintf(pOut, " { %*u, %2u, %u, %2u, %4u, %#06x }, /* ",
+ fBigTable ? 7 : 2,
+ pChild->uKey,
+ (unsigned)pChild->StrTabEntry.cchString,
+ pChild->fChildrenInBigTable,
+ pChild->cChildren,
+ pChild->idxChildren,
+ pChild->StrTabEntry.offStrTab);
+ writeDottedOidForNode(pChild, pOut);
+ if (pChild->StrTabEntry.pszString)
+ {
+ fputs(" = \"", pOut);
+ BldProgStrTab_PrintCStringLitteral(pStrTab, &pChild->StrTabEntry, pOut);
+ fputs("\" */\n", pOut);
+ }
+ else
+ fputs(" */\n", pOut);
+ }
+ }
+
+ /*
+ * Then we decend and let our children do the same.
+ */
+ for (unsigned i = 0; i < pCurNode->cChildren; i++)
+ writeOidTree(pCurNode->papChildren[i], pOut, fBigTable, pStrTab);
+}
+
+
+static uint32_t prepareOidTreeForWriting(PRAWOIDNODE pCurNode, uint32_t idxCur, bool fBigTable)
+{
+ if (pCurNode->fChildrenInBigTable == fBigTable)
+ {
+ pCurNode->idxChildren = pCurNode->cChildren ? idxCur : 0;
+ idxCur += pCurNode->cChildren;
+ }
+
+ for (unsigned i = 0; i < pCurNode->cChildren; i++)
+ idxCur = prepareOidTreeForWriting(pCurNode->papChildren[i], idxCur, fBigTable);
+
+ return idxCur;
+}
+
+
+static void addStringFromOidTree(PRAWOIDNODE pCurNode, PBLDPROGSTRTAB pStrTab)
+{
+ /* Do self. */
+ if (pCurNode->StrTabEntry.pszString)
+ BldProgStrTab_AddString(pStrTab, &pCurNode->StrTabEntry);
+
+ /* Recurse into children. */
+ unsigned i = pCurNode->cChildren;
+ while (i-- > 0)
+ addStringFromOidTree(pCurNode->papChildren[i], pStrTab);
+}
+
+
+static bool isNiceAsciiString(const char *psz)
+{
+ unsigned uch;
+ while ((uch = *psz) != '\0')
+ if ( !(uch & 0x80)
+ && ( uch >= 0x20
+ || uch == '\t') )
+ psz++;
+ else
+ return false;
+ return true;
+}
+
+
+static RTEXITCODE addOidToTree(uint32_t const *pauComponents, unsigned cComponents, const char *pszName,
+ const char *pszFile, unsigned iLineNo)
+{
+ /*
+ * Check preconditions.
+ */
+ size_t cchName = strlen(pszName);
+ if (cchName == '\0')
+ return warning("%s(%d): Empty OID name!\n", pszFile, iLineNo);
+ if (cchName >= BLDPROG_STRTAB_MAX_STRLEN)
+ return warning("%s(%d): OID name is too long (%u)!\n", pszFile, iLineNo, (unsigned)cchName);
+ if (cComponents == 0)
+ return warning("%s(%d): 'Description' without valid OID preceeding it!\n", pszFile, iLineNo);
+ if (!isNiceAsciiString(pszName))
+ return warning("%s(%d): Contains unwanted characters!\n", pszFile, iLineNo);
+
+ /*
+ * Make sure we've got a root node (it has no actual OID componet value,
+ * it's just a place to put the top level children).
+ */
+ if (!g_pOidRoot)
+ {
+ g_pOidRoot = (PRAWOIDNODE)calloc(sizeof(*g_pOidRoot), 1);
+ if (!g_pOidRoot)
+ return error("Out of memory!\n");
+ }
+
+ /*
+ * Decend into the tree, adding any missing nodes as we go along.
+ * We'll end up with the node which is being named.
+ */
+ PRAWOIDNODE pCur = g_pOidRoot;
+ while (cComponents-- > 0)
+ {
+ uint32_t const uKey = *pauComponents++;
+ uint32_t i = pCur->cChildren;
+ while ( i > 0
+ && pCur->papChildren[i - 1]->uKey >= uKey)
+ i--;
+ if ( i < pCur->cChildren
+ && pCur->papChildren[i]->uKey == uKey)
+ pCur = pCur->papChildren[i];
+ else
+ {
+ /* Resize the child pointer array? */
+ if ((pCur->cChildren % 16) == 0)
+ {
+ void *pvNew = realloc(pCur->papChildren, sizeof(pCur->papChildren[0]) * (pCur->cChildren + 16));
+ if (!pvNew)
+ return error("Out of memory!\n");
+ pCur->papChildren = (PRAWOIDNODE *)pvNew;
+ }
+
+ /* Allocate and initialize the node. */
+ PRAWOIDNODE pNew = (PRAWOIDNODE)malloc(sizeof(*pNew));
+ if (!pNew)
+ return error("Out of memory!\n");
+ pNew->uKey = uKey;
+ pNew->pParent = pCur;
+ pNew->papChildren = NULL;
+ pNew->cChildren = 0;
+ pNew->fChildrenInBigTable = false;
+ memset(&pNew->StrTabEntry, 0, sizeof(pNew->StrTabEntry));
+
+ /* Insert it. */
+ if (i < pCur->cChildren)
+ memmove(&pCur->papChildren[i + 1], &pCur->papChildren[i], (pCur->cChildren - i) * sizeof(pCur->papChildren[0]));
+ pCur->papChildren[i] = pNew;
+ pCur->cChildren++;
+
+ if (pCur->cChildren > g_cMaxOidChildren)
+ g_cMaxOidChildren = pCur->cChildren;
+ g_cOidNodes++;
+ if (uKey < 64)
+ g_cOidNodiesWith6bitKeys++;
+ else
+ {
+ pCur->fChildrenInBigTable = true;
+ if (!pCur->pParent)
+ return error("Invalid OID! Top level componet value is out of range: %u (max 2)\n", uKey);
+ }
+
+ /* Decend (could optimize insertion of the remaining nodes, but
+ too much work for very little gain). */
+ pCur = pNew;
+ }
+ }
+
+ /*
+ * Update the node.
+ */
+ if (!pCur->StrTabEntry.pszString)
+ {
+ pCur->StrTabEntry.pszString = (char *)malloc(cchName + 1);
+ if (pCur->StrTabEntry.pszString)
+ memcpy(pCur->StrTabEntry.pszString, pszName, cchName + 1);
+ else
+ return error("Out of memory!\n");
+ pCur->StrTabEntry.cchString = cchName;
+ if (cchName >= 64)
+ pCur->fChildrenInBigTable = true;
+ g_cOidNodesWithStrings++;
+ }
+ /* Ignore duplicates, but warn if different name. */
+ else if ( pCur->StrTabEntry.cchString != cchName
+ || strcmp(pszName, pCur->StrTabEntry.pszString) != 0)
+ warning("%s(%d): Duplicate OID, name differs: '%s' vs '%s'\n", pszFile, iLineNo, pCur->StrTabEntry.pszString, pszName);
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+static RTEXITCODE parseOid(uint32_t *pauComponents, unsigned *pcComponents, unsigned cMaxComponents, char const *pszOid,
+ const char *pszFile, unsigned iLine)
+{
+ const char *pszInput = pszOid;
+ unsigned i = 0;
+ char ch;
+ for (;;)
+ {
+ /*
+ * Parse the value.
+ */
+ unsigned uValue = 0;
+ if (RT_C_IS_DIGIT((ch = *pszOid)))
+ {
+ do
+ {
+ uValue *= 10;
+ uValue += ch - '0';
+ if (uValue < OID2C_MAX_COMP_VALUE)
+ pszOid++;
+ else
+ return warning("%s(%d): Component %u in OID attribute value '%s' is out side the supported!\n",
+ pszFile, iLine, i, pszInput);
+ } while (RT_C_IS_DIGIT((ch = *pszOid)));
+ if ( ch == '\0'
+ || ch == '.'
+ || RT_C_IS_BLANK(ch))
+ {
+ if (i < cMaxComponents)
+ {
+ pauComponents[i] = uValue;
+ i++;
+ if (ch != '\0')
+ pszOid++;
+ else
+ {
+ *pcComponents = i;
+ return RTEXITCODE_SUCCESS;
+ }
+ }
+ else
+ return warning("%s(%d): Too many OID components in '%s'!\n", pszFile, iLine, pszInput);
+ }
+ else
+ return warning("%s(%d): Invalid OID attribute value '%s' (ch=%c)!\n", pszFile, iLine, pszInput, ch);
+ }
+ else
+ return warning("%s(%d): Invalid OID attribute value '%s' (ch=%c)!\n", pszFile, iLine, pszInput, ch);
+ }
+}
+
+
+static RTEXITCODE loadOidFile(FILE *pIn, const char *pszFile)
+{
+ /*
+ * We share the format used by dumpasn1.cfg, except that we accept
+ * dotted OIDs.
+ *
+ * An OID entry starts with a 'OID = <space or dot separated OID>'.
+ * It is usually followed by an 'Comment = ', which we ignore, and a
+ * 'Description = <name>' which we keep. We save the entry once we
+ * see the description attribute.
+ */
+ unsigned cOidComponents = 0;
+ uint32_t auOidComponents[16];
+ unsigned iLineNo = 0;
+ char szLine[16384];
+ char *pszLine;
+ szLine[sizeof(szLine) - 1] = '\0';
+ while ((pszLine = fgets(szLine, sizeof(szLine) - 1, pIn)) != NULL)
+ {
+ iLineNo++;
+
+ /* Strip leading spaces.*/
+ char ch;
+ while (RT_C_IS_SPACE((ch = *pszLine)) )
+ pszLine++;
+
+ /* We only care about lines starting with 'OID =', 'Description =' or
+ a numbered OID. */
+ if ( ch == 'O' || ch == 'o'
+ || ch == 'D' || ch == 'd'
+ || ch == '0' || ch == '1' || ch == '2')
+ {
+ /* Right strip the line. */
+ size_t cchLine = strlen(pszLine);
+ while (cchLine > 0 && RT_C_IS_SPACE(pszLine[cchLine - 1]))
+ cchLine--;
+ pszLine[cchLine] = '\0';
+
+ /* Separate the attribute name from the value. */
+ char *pszValue = (char *)memchr(pszLine, '=', cchLine);
+ if (pszValue)
+ {
+ size_t cchName = pszValue - pszLine;
+
+ /* Right strip the name. */
+ while (cchName > 0 && RT_C_IS_SPACE(pszLine[cchName - 1]))
+ cchName--;
+ pszLine[cchName] = '\0';
+
+ /* Left strip the value. */
+ do
+ pszValue++;
+ while (RT_C_IS_SPACE(*pszValue));
+
+ /* Attribute switch */
+ if ( cchName == 3
+ && (pszLine[0] == 'O' || pszLine[0] == 'o')
+ && (pszLine[1] == 'I' || pszLine[1] == 'i')
+ && (pszLine[2] == 'D' || pszLine[2] == 'd'))
+ {
+ cOidComponents = 0;
+ parseOid(auOidComponents, &cOidComponents, RT_ELEMENTS(auOidComponents), pszValue, pszFile, iLineNo);
+ }
+ else if ( cchName == 11
+ && (pszLine[0] == 'D' || pszLine[0] == 'd')
+ && (pszLine[1] == 'e' || pszLine[1] == 'E')
+ && (pszLine[2] == 's' || pszLine[2] == 'S')
+ && (pszLine[3] == 'c' || pszLine[3] == 'C')
+ && (pszLine[4] == 'r' || pszLine[4] == 'R')
+ && (pszLine[5] == 'i' || pszLine[5] == 'I')
+ && (pszLine[6] == 'p' || pszLine[6] == 'P')
+ && (pszLine[7] == 't' || pszLine[7] == 'T')
+ && (pszLine[8] == 'i' || pszLine[8] == 'I')
+ && (pszLine[9] == 'o' || pszLine[9] == 'O')
+ && (pszLine[10] == 'n' || pszLine[10] == 'N'))
+ {
+ if ( addOidToTree(auOidComponents, cOidComponents, pszValue, pszFile, iLineNo)
+ != RTEXITCODE_SUCCESS)
+ return RTEXITCODE_FAILURE;
+ cOidComponents = 0;
+ }
+ else
+ {
+ /* <OID> = <Value> */
+ cOidComponents = 0;
+ if ( parseOid(auOidComponents, &cOidComponents, RT_ELEMENTS(auOidComponents), pszLine, pszLine, iLineNo)
+ == RTEXITCODE_SUCCESS)
+ {
+ if ( addOidToTree(auOidComponents, cOidComponents, pszValue, pszFile, iLineNo)
+ != RTEXITCODE_SUCCESS)
+ return RTEXITCODE_FAILURE;
+ }
+ cOidComponents = 0;
+ }
+ }
+ }
+
+ }
+ if (feof(pIn))
+ return RTEXITCODE_SUCCESS;
+ return error("error or something reading '%s'.\n", pszFile);
+}
+
+
+
+static RTEXITCODE usage(FILE *pOut, const char *argv0, RTEXITCODE rcExit)
+{
+ fprintf(pOut, "usage: %s <out-file.c> <oid-file> [oid-file2 [...]]\n", argv0);
+ return rcExit;
+}
+
+int main(int argc, char **argv)
+{
+ /*
+ * Process arguments and input files.
+ */
+ bool fVerbose = false;
+ unsigned cInFiles = 0;
+ const char *pszOutFile = NULL;
+ for (int i = 1; i < argc; i++)
+ {
+ const char *pszFile = NULL;
+ if (argv[i][0] != '-')
+ pszFile = argv[i];
+ else if (!strcmp(argv[i], "-"))
+ pszFile = argv[i];
+ else
+ return usage(stderr, argv[0], RTEXITCODE_SYNTAX);
+
+ if (!pszOutFile)
+ pszOutFile = pszFile;
+ else
+ {
+ cInFiles++;
+ FILE *pInFile = fopen(pszFile, "r");
+ if (!pInFile)
+ return error("opening '%s' for reading.\n", pszFile);
+ RTEXITCODE rcExit = loadOidFile(pInFile, pszFile);
+ fclose(pInFile);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ }
+ }
+
+ /*
+ * Check that the user specified at least one input and an output file.
+ */
+ if (!pszOutFile)
+ return error("No output file specified specified!\n");
+ if (!cInFiles)
+ return error("No input files specified!\n");
+ if (!g_cOidNodes)
+ return error("No OID found!\n");
+ if (fVerbose)
+ printf("debug: %u nodes with strings; %u nodes without strings; %u nodes in total;\n"
+ "debug: max %u children; %u nodes with 6-bit keys (%u others)\n",
+ g_cOidNodesWithStrings, g_cOidNodes - g_cOidNodesWithStrings, g_cOidNodes,
+ g_cMaxOidChildren, g_cOidNodiesWith6bitKeys, g_cOidNodes - g_cOidNodiesWith6bitKeys);
+
+ /*
+ * Compile the string table.
+ */
+ BLDPROGSTRTAB StrTab;
+ if (!BldProgStrTab_Init(&StrTab, g_cOidNodesWithStrings))
+ return error("Out of memory!\n");
+
+ addStringFromOidTree(g_pOidRoot, &StrTab);
+
+ if (!BldProgStrTab_CompileIt(&StrTab, fVerbose))
+ return error("BldProgStrTab_CompileIt failed!\n");
+
+ /*
+ * Open the output file and write out the stuff.
+ */
+ FILE *pOut;
+ if (!strcmp(pszOutFile, "-"))
+ pOut = stdout;
+ else
+ pOut = fopen(pszOutFile, "w");
+ if (!pOut)
+ return error("opening '%s' for writing.\n", pszOutFile);
+
+ /* Write the string table. */
+ BldProgStrTab_WriteStringTable(&StrTab, pOut, "static ", "g_", "OidDbStrTab");
+
+ prepareOidTreeForWriting(g_pOidRoot, 0, false /*fBigTable*/);
+ prepareOidTreeForWriting(g_pOidRoot, 0, true /*fBigTable*/);
+
+ fprintf(pOut,
+ "\n"
+ "#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)\n"
+ "# pragma pack(2)\n"
+ "#endif\n"
+ "typedef struct RTOIDENTRYSMALL\n"
+ "{\n"
+ " uint32_t uKey : 6;\n"
+ " uint32_t cchString : 6;\n"
+ " uint32_t fBigTable : 1;\n"
+ " uint32_t cChildren : 7;\n"
+ " uint32_t idxChildren : 12;\n"
+ " uint16_t offString;\n"
+ "} RTOIDENTRYSMALL;\n"
+ "#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)\n"
+ "# pragma pack()\n"
+ "AssertCompileSize(RTOIDENTRYSMALL, 6);\n"
+ "#endif\n"
+ "typedef RTOIDENTRYSMALL const *PCRTOIDENTRYSMALL;\n"
+ "\n"
+ "static const RTOIDENTRYSMALL g_aSmallOidTable[] = \n{\n");
+ writeOidTree(g_pOidRoot, pOut, false /*fBigTable*/, &StrTab);
+ fprintf(pOut, "};\n");
+
+ fprintf(pOut,
+ "\n"
+ "#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)\n"
+ "# pragma pack(2)\n"
+ "#endif\n"
+ "typedef struct RTOIDENTRYBIG\n"
+ "{\n"
+ " uint32_t uKey;\n"
+ " uint8_t cchString;\n"
+ " uint8_t fBigTable : 1;\n"
+ " uint8_t cChildren : 7;\n"
+ " uint16_t idxChildren;\n"
+ " uint16_t offString;\n"
+ "} RTOIDENTRYBIG;\n"
+ "#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)\n"
+ "# pragma pack()\n"
+ "AssertCompileSize(RTOIDENTRYBIG, 10);\n"
+ "#endif\n"
+ "typedef RTOIDENTRYBIG const *PCRTOIDENTRYBIG;\n"
+ "\n"
+ "static const RTOIDENTRYBIG g_aBigOidTable[] = \n{\n");
+ writeOidTree(g_pOidRoot, pOut, true /*fBigTable*/, &StrTab);
+ fprintf(pOut, "};\n");
+
+ /* Carefully close the output file. */
+ if (ferror(pOut))
+ return error("problem writing '%s'!\n", pszOutFile);
+ if (fclose(pOut) != 0)
+ return error("closing '%s' after writing it.\n", pszOutFile);
+
+ return RTEXITCODE_SUCCESS;
+}
+