diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 14:19:18 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 14:19:18 +0000 |
commit | 4035b1bfb1e5843a539a8b624d21952b756974d1 (patch) | |
tree | f1e9cd5bf548cbc57ff2fddfb2b4aa9ae95587e2 /src/VBox/Runtime/common/asn1/asn1-ut-bitstring.cpp | |
parent | Initial commit. (diff) | |
download | virtualbox-upstream.tar.xz virtualbox-upstream.zip |
Adding upstream version 6.1.22-dfsg.upstream/6.1.22-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Runtime/common/asn1/asn1-ut-bitstring.cpp')
-rw-r--r-- | src/VBox/Runtime/common/asn1/asn1-ut-bitstring.cpp | 518 |
1 files changed, 518 insertions, 0 deletions
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..91dd2d32 --- /dev/null +++ b/src/VBox/Runtime/common/asn1/asn1-ut-bitstring.cpp @@ -0,0 +1,518 @@ +/* $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-2020 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE 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. + */ + + +/********************************************************************************************************************************* +* 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_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> + |