VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/asn1/asn1-ut-bitstring.cpp@ 59675

Last change on this file since 59675 was 59675, checked in by vboxsync, 9 years ago

RTAsn1BitString_EncodeWrite: Fixed assertion. Added testcase which triggers it.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.7 KB
Line 
1/* $Id: asn1-ut-bitstring.cpp 59675 2016-02-15 10:39:32Z vboxsync $ */
2/** @file
3 * IPRT - ASN.1, Bit String Type.
4 *
5 * @remarks This file should remain very similar to asn1-ut-octetstring.cpp.
6 */
7
8/*
9 * Copyright (C) 2006-2015 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 *
19 * The contents of this file may alternatively be used under the terms
20 * of the Common Development and Distribution License Version 1.0
21 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
22 * VirtualBox OSE distribution, in which case the provisions of the
23 * CDDL are applicable instead of those of the GPL.
24 *
25 * You may elect to license modified versions of this file under the
26 * terms and conditions of either the GPL or the CDDL or both.
27 */
28
29
30/*********************************************************************************************************************************
31* Header Files *
32*********************************************************************************************************************************/
33#include "internal/iprt.h"
34#include <iprt/asn1.h>
35
36#include <iprt/alloca.h>
37#include <iprt/bignum.h>
38#include <iprt/ctype.h>
39#include <iprt/err.h>
40#include <iprt/string.h>
41#include <iprt/uni.h>
42
43#include <iprt/formats/asn1.h>
44
45
46/*********************************************************************************************************************************
47* Structures and Typedefs *
48*********************************************************************************************************************************/
49typedef struct RTASN1BITSTRINGWRITERCTX
50{
51 /** Pointer to the output buffer. */
52 uint8_t *pbBuf;
53 /** The current buffer offset. */
54 uint32_t offBuf;
55 /** The size of the buffer. */
56 uint32_t cbBuf;
57} RTASN1BITSTRINGWRITERCTX;
58
59
60/** @callback_method_impl{FNRTASN1ENCODEWRITER,
61 * Used to refresh the content of octet and bit strings. } */
62static DECLCALLBACK(int) rtAsn1BitStringEncodeWriter(const void *pvBuf, size_t cbToWrite, void *pvUser, PRTERRINFO pErrInfo)
63{
64 RTASN1BITSTRINGWRITERCTX *pCtx = (RTASN1BITSTRINGWRITERCTX *)pvUser;
65 AssertReturn(cbToWrite <= pCtx->cbBuf - pCtx->offBuf,
66 RTErrInfoSetF(pErrInfo, VERR_BUFFER_OVERFLOW,
67 "cbToWrite=%#x offBuf=%#x cbBuf=%#x", cbToWrite, pCtx->cbBuf, pCtx->offBuf));
68 memcpy(&pCtx->pbBuf[pCtx->offBuf], pvBuf, cbToWrite);
69 pCtx->offBuf += (uint32_t)cbToWrite;
70 return VINF_SUCCESS;
71}
72
73
74/** @callback_method_impl{FNRTASN1ENCODEWRITER,
75 * Used to compare the encoded raw content of an octet or bit string with the
76 * encapsulated object. } */
77static DECLCALLBACK(int) rtAsn1BitStringEncodeCompare(const void *pvBuf, size_t cbToWrite, void *pvUser, PRTERRINFO pErrInfo)
78{
79 RTASN1BITSTRINGWRITERCTX *pCtx = (RTASN1BITSTRINGWRITERCTX *)pvUser;
80 AssertReturn(cbToWrite <= pCtx->cbBuf - pCtx->offBuf, VERR_BUFFER_OVERFLOW);
81 if (memcmp(&pCtx->pbBuf[pCtx->offBuf], pvBuf, cbToWrite) != 0)
82 return VERR_NOT_EQUAL;
83 pCtx->offBuf += (uint32_t)cbToWrite;
84 return VINF_SUCCESS;
85}
86
87
88
89/*
90 * ASN.1 BIT STRING - Special Methods.
91 */
92
93RTDECL(uint64_t) RTAsn1BitString_GetAsUInt64(PCRTASN1BITSTRING pThis)
94{
95 /*
96 * Extract the first 64 bits in host order.
97 */
98 uint8_t const *pb = pThis->uBits.pu8;
99 uint64_t uRet = 0;
100 uint32_t cShift = 0;
101 uint32_t cBits = RT_MIN(pThis->cBits, 64);
102 while (cBits > 0)
103 {
104 uint8_t b = *pb++;
105#if 1 /* We don't have a bit-order constant... */
106 b = ((b & 0x01) << 7)
107 | ((b & 0x02) << 5)
108 | ((b & 0x04) << 3)
109 | ((b & 0x08) << 1)
110 | ((b & 0x10) >> 1)
111 | ((b & 0x20) >> 3)
112 | ((b & 0x40) >> 5)
113 | ((b & 0x80) >> 7);
114#endif
115 if (cBits < 8)
116 {
117 b &= RT_BIT_32(cBits) - 1;
118 uRet |= (uint64_t)b << cShift;
119 break;
120 }
121 uRet |= (uint64_t)b << cShift;
122 cShift += 8;
123 cBits -= 8;
124 }
125
126 return uRet;
127}
128
129
130RTDECL(int) RTAsn1BitString_RefreshContent(PRTASN1BITSTRING pThis, uint32_t fFlags,
131 PCRTASN1ALLOCATORVTABLE pAllocator, PRTERRINFO pErrInfo)
132{
133 AssertReturn(pThis->pEncapsulated, VERR_INVALID_STATE);
134
135 uint32_t cbEncoded;
136 int rc = RTAsn1EncodePrepare(pThis->pEncapsulated, fFlags, &cbEncoded, pErrInfo);
137 if (RT_SUCCESS(rc))
138 {
139 pThis->Asn1Core.cb = 1 + cbEncoded;
140 pThis->cBits = cbEncoded * 8;
141 AssertReturn(pThis->cBits / 8 == cbEncoded, RTErrInfoSetF(pErrInfo, VERR_TOO_MUCH_DATA, "cbEncoded=%#x", cbEncoded));
142
143 rc = RTAsn1ContentReallocZ(&pThis->Asn1Core, cbEncoded + 1, pAllocator);
144 if (RT_SUCCESS(rc))
145 {
146 pThis->uBits.pu8 = pThis->Asn1Core.uData.pu8 + 1;
147
148 /* Initialize the writer context and write the first byte concerning unused bits. */
149 RTASN1BITSTRINGWRITERCTX Ctx;
150 Ctx.pbBuf = (uint8_t *)pThis->Asn1Core.uData.pu8;
151 Ctx.cbBuf = cbEncoded + 1;
152 Ctx.offBuf = 1;
153 *Ctx.pbBuf = 0;
154
155 rc = RTAsn1EncodeWrite(pThis->pEncapsulated, fFlags, rtAsn1BitStringEncodeWriter, &Ctx, pErrInfo);
156 if (RT_SUCCESS(rc))
157 {
158 if (Ctx.offBuf == cbEncoded + 1)
159 return VINF_SUCCESS;
160
161 rc = RTErrInfoSetF(pErrInfo, rc, "Expected %#x + 1 bytes, got %#x", cbEncoded, Ctx.offBuf);
162 }
163 }
164 else
165 rc = RTErrInfoSetF(pErrInfo, rc, "Error allocating %#x + 1 bytes for storing content\n", cbEncoded);
166 }
167 return rc;
168}
169
170
171RTDECL(bool) RTAsn1BitString_AreContentBitsValid(PCRTASN1BITSTRING pThis, uint32_t fFlags)
172{
173 if (pThis->pEncapsulated)
174 {
175 if (pThis->cBits & 7)
176 return false;
177
178 /* Check the encoded length of the bits. */
179 uint32_t cbEncoded;
180 int rc = RTAsn1EncodePrepare(pThis->pEncapsulated, fFlags, &cbEncoded, NULL);
181 if (RT_FAILURE(rc))
182 return false;
183 if (pThis->Asn1Core.cb != 1 + cbEncoded)
184 return false;
185
186 /* Check the encoded bits, if there are any. */
187 if (cbEncoded)
188 {
189 if (!pThis->Asn1Core.uData.pv)
190 return false;
191
192 /* Check the first byte, the unused bit count. */
193 if (*pThis->Asn1Core.uData.pu8 != 0)
194 return false;
195
196 /* Check the other bytes. */
197 RTASN1BITSTRINGWRITERCTX Ctx;
198 Ctx.pbBuf = (uint8_t *)pThis->Asn1Core.uData.pu8;
199 Ctx.cbBuf = cbEncoded + 1;
200 Ctx.offBuf = 1;
201 rc = RTAsn1EncodeWrite(pThis->pEncapsulated, fFlags, rtAsn1BitStringEncodeCompare, &Ctx, NULL);
202 if (RT_FAILURE(rc))
203 return false;
204 }
205 }
206 return true;
207}
208
209
210
211
212/*
213 * ASN.1 BIT STRING - Standard Methods.
214 */
215
216/** @interface_method_impl{FNRTASN1COREVTENCODEPREP} */
217static DECLCALLBACK(int) RTAsn1BitString_EncodePrep(PRTASN1CORE pThisCore, uint32_t fFlags, PRTERRINFO pErrInfo)
218{
219 PRTASN1BITSTRING pThis = (PRTASN1BITSTRING)pThisCore;
220 if (!pThis->pEncapsulated)
221 {
222 Assert(pThis->cBits == 0 || pThis->Asn1Core.uData.pv);
223 return VINF_SUCCESS;
224 }
225
226 /* Figure out the size of the encapsulated content. */
227 uint32_t cbEncoded;
228 int rc = RTAsn1EncodePrepare(pThis->pEncapsulated, fFlags, &cbEncoded, pErrInfo);
229 if (RT_SUCCESS(rc))
230 {
231 /* Free the bytes if they don't match up. */
232 if (pThis->Asn1Core.uData.pv)
233 {
234 bool fMustFree = pThis->Asn1Core.cb != 1 + cbEncoded || (pThis->cBits & 7);
235 if (!fMustFree)
236 {
237 RTASN1BITSTRINGWRITERCTX Ctx;
238 Ctx.pbBuf = (uint8_t *)pThis->Asn1Core.uData.pu8;
239 Ctx.cbBuf = 1 + cbEncoded;
240 Ctx.offBuf = 1;
241 fMustFree = *Ctx.pbBuf != 0;
242 if (!fMustFree)
243 {
244 rc = RTAsn1EncodeWrite(pThis->pEncapsulated, fFlags, rtAsn1BitStringEncodeCompare, &Ctx, NULL);
245 fMustFree = RT_FAILURE_NP(rc);
246 }
247 }
248 if (fMustFree)
249 {
250 pThis->uBits.pv = NULL;
251 RTAsn1ContentFree(&pThis->Asn1Core);
252 }
253 }
254 pThis->Asn1Core.cb = 1 + cbEncoded;
255 pThis->cBits = cbEncoded * 8;
256
257 rc = RTAsn1EncodeRecalcHdrSize(&pThis->Asn1Core, fFlags, pErrInfo);
258 }
259 return rc;
260}
261
262
263/** @interface_method_impl{FNRTASN1COREVTENCODEWRITE} */
264static DECLCALLBACK(int) RTAsn1BitString_EncodeWrite(PRTASN1CORE pThisCore, uint32_t fFlags, PFNRTASN1ENCODEWRITER pfnWriter,
265 void *pvUser, PRTERRINFO pErrInfo)
266{
267 PRTASN1BITSTRING pThis = (PRTASN1BITSTRING)pThisCore;
268
269 AssertReturn(RT_ALIGN(pThis->cBits, 8) / 8 + 1 == pThis->Asn1Core.cb, VERR_INTERNAL_ERROR_3);
270
271 /*
272 * First the header.
273 */
274 int rc = RTAsn1EncodeWriteHeader(&pThis->Asn1Core, fFlags, pfnWriter, pvUser, pErrInfo);
275 if (RT_SUCCESS(rc) && rc != VINF_ASN1_NOT_ENCODED)
276 {
277 /*
278 * The content starts with an unused bit count. Calculate it in case we
279 * need to write it out.
280 */
281 uint8_t cUnusedBits = 0;
282 if ((pThis->cBits & 7) != 0)
283 cUnusedBits = 8 - (pThis->cBits & 7);
284
285 /*
286 * If nothing is encapsulated, the core points to the content (if we have any).
287 */
288 if (!pThis->pEncapsulated)
289 {
290 if (pThis->cBits > 0)
291 {
292 Assert(pThis->Asn1Core.uData.pu8[0] == cUnusedBits);
293 rc = pfnWriter(pThis->Asn1Core.uData.pu8, pThis->Asn1Core.cb, pvUser, pErrInfo);
294 }
295 else
296 rc = pfnWriter(&cUnusedBits, sizeof(cUnusedBits), pvUser, pErrInfo);
297 }
298 /*
299 * Write the unused bit count and then call upon the encapsulated
300 * content to serialize itself.
301 */
302 else
303 {
304 rc = pfnWriter(&cUnusedBits, sizeof(cUnusedBits), pvUser, pErrInfo);
305 if (RT_SUCCESS(rc))
306 rc = RTAsn1EncodeWrite(pThis->pEncapsulated, fFlags, pfnWriter, pvUser, pErrInfo);
307 }
308 }
309 return rc;
310}
311
312
313RT_DECL_DATA_CONST(RTASN1COREVTABLE const) g_RTAsn1BitString_Vtable =
314{
315 "RTAsn1BitString",
316 sizeof(RTASN1BITSTRING),
317 ASN1_TAG_BIT_STRING,
318 ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
319 0,
320 (PFNRTASN1COREVTDTOR)RTAsn1BitString_Delete,
321 (PFNRTASN1COREVTENUM)RTAsn1BitString_Enum,
322 (PFNRTASN1COREVTCLONE)RTAsn1BitString_Clone,
323 (PFNRTASN1COREVTCOMPARE)RTAsn1BitString_Compare,
324 (PFNRTASN1COREVTCHECKSANITY)RTAsn1BitString_CheckSanity,
325 RTAsn1BitString_EncodePrep,
326 RTAsn1BitString_EncodeWrite
327};
328
329
330RTDECL(int) RTAsn1BitString_Init(PRTASN1BITSTRING pThis, PCRTASN1ALLOCATORVTABLE pAllocator)
331{
332 RT_ZERO(*pThis);
333
334 RTAsn1Core_InitEx(&pThis->Asn1Core, ASN1_TAG_BIT_STRING, ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
335 &g_RTAsn1BitString_Vtable, RTASN1CORE_F_PRESENT | RTASN1CORE_F_PRIMITE_TAG_STRUCT);
336 /*pThis->cBits = 0;
337 pThis->cMaxBits = 0;
338 pThis->uBits.pv = NULL;
339 pThis->pEncapsulated = NULL; */
340 RTAsn1MemInitAllocation(&pThis->EncapsulatedAllocation, pAllocator);
341
342 return VINF_SUCCESS;
343}
344
345
346RTDECL(int) RTAsn1BitString_Clone(PRTASN1BITSTRING pThis, PCRTASN1BITSTRING pSrc, PCRTASN1ALLOCATORVTABLE pAllocator)
347{
348 AssertPtr(pSrc); AssertPtr(pThis); AssertPtr(pAllocator);
349
350 RT_ZERO(*pThis);
351 if (RTAsn1BitString_IsPresent(pSrc))
352 {
353 AssertReturn(pSrc->Asn1Core.pOps == &g_RTAsn1BitString_Vtable, VERR_INTERNAL_ERROR_3);
354
355 int rc;
356 if (!pSrc->pEncapsulated)
357 rc = RTAsn1Core_CloneContent(&pThis->Asn1Core, &pSrc->Asn1Core, pAllocator);
358 else
359 rc = RTAsn1Core_CloneNoContent(&pThis->Asn1Core, &pSrc->Asn1Core);
360 if (RT_FAILURE(rc))
361 return rc;
362
363 RTAsn1MemInitAllocation(&pThis->EncapsulatedAllocation, pAllocator);
364 pThis->cBits = pSrc->cBits;
365 pThis->cMaxBits = pSrc->cMaxBits;
366 if (!pSrc->pEncapsulated)
367 pThis->uBits.pv = pThis->Asn1Core.uData.pu8 ? pThis->Asn1Core.uData.pu8 + 1 : NULL;
368 else
369 {
370 PCRTASN1COREVTABLE pOps = pSrc->pEncapsulated->pOps;
371 Assert(!pOps || pOps->pfnClone);
372 if (pOps && pOps->pfnClone)
373 {
374 /* We can clone the decoded encapsulated object. */
375 rc = RTAsn1MemAllocZ(&pThis->EncapsulatedAllocation, (void **)&pThis->pEncapsulated, pOps->cbStruct);
376 if (RT_SUCCESS(rc))
377 {
378 rc = pOps->pfnClone(pThis->pEncapsulated, pSrc->pEncapsulated, pAllocator);
379 if (RT_FAILURE(rc))
380 RTAsn1MemFree(&pThis->EncapsulatedAllocation, pThis->pEncapsulated);
381 }
382 }
383 else
384 {
385 /* Borrow the encapsulated pointer and use RTAsn1BitString_RefreshContent
386 to get an accurate copy of the bytes. */
387 pThis->pEncapsulated = pSrc->pEncapsulated;
388 rc = RTAsn1BitString_RefreshContent(pThis, RTASN1ENCODE_F_DER, pAllocator, NULL);
389 pThis->pEncapsulated = NULL;
390 }
391 if (RT_FAILURE(rc))
392 {
393 RTAsn1ContentFree(&pThis->Asn1Core);
394 RT_ZERO(*pThis);
395 return rc;
396 }
397 }
398 }
399 return VINF_SUCCESS;
400}
401
402
403RTDECL(void) RTAsn1BitString_Delete(PRTASN1BITSTRING pThis)
404{
405 if ( pThis
406 && RTAsn1BitString_IsPresent(pThis))
407 {
408 Assert(pThis->Asn1Core.pOps == &g_RTAsn1BitString_Vtable);
409
410 /* Destroy the encapsulated object. */
411 if (pThis->pEncapsulated)
412 {
413 RTAsn1VtDelete(pThis->pEncapsulated);
414 if (pThis->EncapsulatedAllocation.cbAllocated)
415 RTAsn1MemFree(&pThis->EncapsulatedAllocation, pThis->pEncapsulated);
416 }
417
418 /* Delete content and wipe the content. */
419 RTAsn1ContentFree(&pThis->Asn1Core);
420 RT_ZERO(*pThis);
421 }
422}
423
424
425RTDECL(int) RTAsn1BitString_Enum(PRTASN1BITSTRING pThis, PFNRTASN1ENUMCALLBACK pfnCallback, uint32_t uDepth, void *pvUser)
426{
427 Assert(pThis && (!RTAsn1BitString_IsPresent(pThis) || pThis->Asn1Core.pOps == &g_RTAsn1BitString_Vtable));
428
429 /* Enumerate the encapsulated object if present. */
430 if (pThis->pEncapsulated)
431 return pfnCallback(pThis->pEncapsulated, "Encapsulated", uDepth + 1, pvUser);
432 return VINF_SUCCESS;
433}
434
435
436RTDECL(int) RTAsn1BitString_Compare(PCRTASN1BITSTRING pLeft, PCRTASN1BITSTRING pRight)
437{
438 Assert(pLeft && (!RTAsn1BitString_IsPresent(pLeft) || pLeft->Asn1Core.pOps == &g_RTAsn1BitString_Vtable));
439 Assert(pRight && (!RTAsn1BitString_IsPresent(pRight) || pRight->Asn1Core.pOps == &g_RTAsn1BitString_Vtable));
440
441 int iDiff;
442 if (RTAsn1BitString_IsPresent(pLeft))
443 {
444 if (RTAsn1BitString_IsPresent(pRight))
445 {
446 /* Since it's really hard to tell whether encapsulated objects have
447 been modified or not, we might have to refresh both objects
448 while doing this compare. We'll try our best to avoid it though. */
449 if (pLeft->pEncapsulated || pRight->pEncapsulated)
450 {
451 if ( pLeft->pEncapsulated
452 && pRight->pEncapsulated
453 && pLeft->pEncapsulated->pOps == pRight->pEncapsulated->pOps)
454 iDiff = pLeft->pEncapsulated->pOps->pfnCompare(pLeft->pEncapsulated, pRight->pEncapsulated);
455 else
456 {
457 /* No direct comparison of encapsulated objects possible,
458 make sure we've got the rigth bytes then. */
459 if ( pLeft->pEncapsulated
460 && !RTAsn1BitString_AreContentBitsValid(pLeft, RTASN1ENCODE_F_DER))
461 {
462 int rc = RTAsn1BitString_RefreshContent((PRTASN1BITSTRING)pLeft, RTASN1ENCODE_F_DER,
463 pLeft->EncapsulatedAllocation.pAllocator, NULL);
464 AssertRC(rc);
465 }
466
467 if ( pRight->pEncapsulated
468 && !RTAsn1BitString_AreContentBitsValid(pRight, RTASN1ENCODE_F_DER))
469 {
470 int rc = RTAsn1BitString_RefreshContent((PRTASN1BITSTRING)pRight, RTASN1ENCODE_F_DER,
471 pRight->EncapsulatedAllocation.pAllocator, NULL);
472 AssertRC(rc);
473 }
474
475 /* Compare the content bytes. */
476 iDiff = RTAsn1Core_CompareEx(&pLeft->Asn1Core, &pRight->Asn1Core, true /*fIgnoreTagAndClass*/);
477 }
478 }
479 /*
480 * No encapsulated object, just compare the raw content bytes.
481 */
482 else
483 iDiff = RTAsn1Core_CompareEx(&pLeft->Asn1Core, &pRight->Asn1Core, true /*fIgnoreTagAndClass*/);
484 }
485 else
486 iDiff = -1;
487 }
488 else
489 iDiff = 0 - (int)RTAsn1BitString_IsPresent(pRight);
490 return iDiff;
491}
492
493
494RTDECL(int) RTAsn1BitString_CheckSanity(PCRTASN1BITSTRING pThis, uint32_t fFlags, PRTERRINFO pErrInfo, const char *pszErrorTag)
495{
496 if (RT_UNLIKELY(!RTAsn1BitString_IsPresent(pThis)))
497 return RTErrInfoSetF(pErrInfo, VERR_ASN1_NOT_PRESENT, "%s: Missing (BIT STRING).", pszErrorTag);
498
499 if (pThis->cBits > pThis->cMaxBits)
500 return RTErrInfoSetF(pErrInfo, VERR_ASN1_BITSTRING_OUT_OF_BOUNDS, "%s: Exceeding max bits: cBits=%u cMaxBits=%u.",
501 pszErrorTag, pThis->cBits, pThis->cMaxBits);
502
503 if (pThis->pEncapsulated)
504 return pThis->pEncapsulated->pOps->pfnCheckSanity(pThis->pEncapsulated, fFlags & RTASN1_CHECK_SANITY_F_COMMON_MASK,
505 pErrInfo, pszErrorTag);
506 return VINF_SUCCESS;
507}
508
509/*
510 * Generate code for the associated collection types.
511 */
512#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-bitstring-template.h"
513#include <iprt/asn1-generator-internal-header.h>
514#include <iprt/asn1-generator-core.h>
515#include <iprt/asn1-generator-init.h>
516#include <iprt/asn1-generator-sanity.h>
517
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette