VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/asn1/asn1-ut-objid.cpp@ 56290

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

IPRT: Updated (C) year.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.4 KB
Line 
1/* $Id: asn1-ut-objid.cpp 56290 2015-06-09 14:01:31Z vboxsync $ */
2/** @file
3 * IPRT - ASN.1, OBJECT IDENTIFIER Type.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27/*******************************************************************************
28* Header Files *
29*******************************************************************************/
30#include "internal/iprt.h"
31#include <iprt/asn1.h>
32
33#include <iprt/alloca.h>
34#include <iprt/bignum.h>
35#include <iprt/ctype.h>
36#include <iprt/err.h>
37#include <iprt/string.h>
38#include <iprt/uni.h>
39
40#include <iprt/formats/asn1.h>
41
42
43/*******************************************************************************
44* Global Variables *
45*******************************************************************************/
46static char const g_szDefault[] = "2.16.840.1.113894";
47static uint32_t const g_auDefault[] = { 2, 16, 840, 1, 113894 };
48static uint8_t const g_abDefault[] =
49{
50 2*40 + 16, 0x80 | (840 >> 7), 840 & 0x7f, 1, 0x80 | (113894 >> 14), 0x80 | ((113894 >> 7) & 0x7f), 113894 & 0x7f
51};
52
53
54/*******************************************************************************
55* Internal Functions *
56*******************************************************************************/
57DECLHIDDEN(int) rtAsn1ObjId_InternalFormatComponent(uint32_t uValue, char **ppszObjId, size_t *pcbObjId); /* asn1-ut-objid.cpp */
58/** @todo check if we really need this. */
59
60
61
62/*
63 * ASN.1 OBJECT IDENTIFIER - Special Methods.
64 */
65
66/**
67 * Encodes the ASN.1 byte sequence for a set of components.
68 *
69 * @returns IPRT status code.
70 * @param cComponents The number of components. Must be at least two.
71 * @param pauComponents The components array.
72 * @param pbEncoded The output buffer.
73 * @param pcbEncoded On input, this holds the size of the output buffer.
74 * On successful return it's the encoded size in bytes.
75 */
76static int rtAsn1ObjId_EncodeComponents(uint32_t cComponents, uint32_t const *pauComponents,
77 uint8_t *pbEncoded, uint32_t *pcbEncoded)
78{
79 uint8_t *pbCur = pbEncoded;
80 uint32_t cbLeft = *pcbEncoded;
81
82 /* The first two componets are encoded together to save a byte, so the loop
83 organization is a little special. */
84 AssertReturn(cComponents >= 2, VERR_ASN1_INTERNAL_ERROR_1);
85 AssertReturn(pauComponents[0] <= 2, VERR_ASN1_INTERNAL_ERROR_1);
86 AssertReturn(pauComponents[1] <= (pauComponents[0] < 2 ? 39 : UINT32_MAX - 80), VERR_ASN1_INTERNAL_ERROR_1);
87 uint32_t i = 1;
88 uint32_t uValue = pauComponents[0] * 40 + pauComponents[1];
89
90 for (;;)
91 {
92 if (uValue < 0x80)
93 {
94 if (RT_UNLIKELY(cbLeft < 1))
95 return VERR_BUFFER_OVERFLOW;
96 cbLeft -= 1;
97 *pbCur++ = (uint8_t)uValue;
98 }
99 else if (uValue < 0x4000)
100 {
101 if (RT_UNLIKELY(cbLeft < 2))
102 return VERR_BUFFER_OVERFLOW;
103 cbLeft -= 2;
104 pbCur[0] = (uValue >> 7) | 0x80;
105 pbCur[1] = uValue & 0x7f;
106 pbCur += 2;
107 }
108 else if (uValue < 0x200000)
109 {
110 if (RT_UNLIKELY(cbLeft < 3))
111 return VERR_BUFFER_OVERFLOW;
112 cbLeft -= 3;
113 pbCur[0] = (uValue >> 14) | 0x80;
114 pbCur[1] = ((uValue >> 7) & 0x7f) | 0x80;
115 pbCur[2] = uValue & 0x7f;
116 pbCur += 3;
117 }
118 else if (uValue < 0x10000000)
119 {
120 if (RT_UNLIKELY(cbLeft < 4))
121 return VERR_BUFFER_OVERFLOW;
122 cbLeft -= 4;
123 pbCur[0] = (uValue >> 21) | 0x80;
124 pbCur[1] = ((uValue >> 14) & 0x7f) | 0x80;
125 pbCur[2] = ((uValue >> 7) & 0x7f) | 0x80;
126 pbCur[3] = uValue & 0x7f;
127 pbCur += 4;
128 }
129 else
130 {
131 if (RT_UNLIKELY(cbLeft < 5))
132 return VERR_BUFFER_OVERFLOW;
133 cbLeft -= 5;
134 pbCur[0] = (uValue >> 28) | 0x80;
135 pbCur[1] = ((uValue >> 21) & 0x7f) | 0x80;
136 pbCur[2] = ((uValue >> 14) & 0x7f) | 0x80;
137 pbCur[3] = ((uValue >> 7) & 0x7f) | 0x80;
138 pbCur[4] = uValue & 0x7f;
139 pbCur += 5;
140 }
141
142 /* Advance / return. */
143 i++;
144 if (i >= cComponents)
145 {
146 *pcbEncoded = (uint32_t)(pbCur - pbEncoded);
147 return VINF_SUCCESS;
148 }
149 uValue = pauComponents[i];
150 }
151}
152
153
154RTDECL(int) RTAsn1ObjId_InitFromString(PRTASN1OBJID pThis, const char *pszObjId, PCRTASN1ALLOCATORVTABLE pAllocator)
155{
156 RT_ZERO(*pThis);
157
158 /*
159 * Check the string, counting the number of components and checking their validity.
160 */
161 size_t cbObjId = strlen(pszObjId) + 1;
162 AssertReturn(cbObjId < sizeof(pThis->szObjId), VERR_ASN1_OBJID_TOO_LONG_STRING_FORM);
163
164 const char *psz = pszObjId;
165
166 /* Special checking of the first component. It has only three valid values: 0,1,2. */
167 char ch = *psz++;
168 if (RT_UNLIKELY(ch < '0' || ch > '2'))
169 return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
170 char const chFirst = ch;
171 ch = *psz++;
172 if (RT_UNLIKELY(ch != '.'))
173 return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
174
175 /* The 2nd component. It the first is 0 or 1, it has a max of 39. */
176 uint32_t cComponents = 1;
177 if (chFirst < '2')
178 {
179 ch = *psz++;
180 if (*psz == '.')
181 {
182 if (RT_UNLIKELY(!RT_C_IS_DIGIT(ch)))
183 return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
184 }
185 else
186 {
187 if (RT_UNLIKELY(ch < '0' || ch > '3'))
188 return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
189 ch = *psz++;
190 if (RT_UNLIKELY(!RT_C_IS_DIGIT(ch)))
191 return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
192 if (*psz != '.')
193 return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
194 }
195 cComponents++;
196 }
197 else
198 psz--;
199
200 /* Subsequent components have max values of UINT32_MAX - 80. */
201 while ((ch = *psz++) != '\0')
202 {
203 if (RT_UNLIKELY(ch != '.'))
204 return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
205 const char *pszStart = psz;
206
207 /* Special treatment of the first digit. Need to make sure it isn't an
208 unnecessary leading 0. */
209 ch = *psz++;
210 if (RT_UNLIKELY(!RT_C_IS_DIGIT(ch)))
211 return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
212 if (RT_UNLIKELY(ch == '0' && RT_C_IS_DIGIT(*psz)))
213 return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
214
215 /* The rest of the digits. */
216 while ((ch = *psz) != '.' && ch != '\0')
217 {
218 if (RT_UNLIKELY(!RT_C_IS_DIGIT(ch)))
219 return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
220 psz++;
221 }
222
223 /* Check the value range. */
224 if (RT_UNLIKELY(psz - pszStart >= 9))
225 if ( psz - pszStart > 9
226 || strncmp(pszStart, "4294967216", 9) >= 0) /* 2^32 - 80 */
227 return VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
228
229 cComponents++;
230 }
231
232 if (RT_UNLIKELY(cComponents >= 128))
233 return VERR_ASN1_OBJID_TOO_MANY_COMPONENTS;
234 pThis->cComponents = (uint8_t)cComponents;
235
236 /*
237 * Find space for the component array, either at the unused end of szObjId
238 * or on the heap.
239 */
240 int rc;
241 RTAsn1MemInitAllocation(&pThis->Allocation, pAllocator);
242#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? */
243 size_t cbLeft = sizeof(pThis->szObjId) - cbObjId;
244 if (cbLeft >= cComponents * sizeof(uint32_t))
245 {
246 pThis->pauComponents = (uint32_t *)&pThis->szObjId[sizeof(pThis->szObjId) - cComponents * sizeof(uint32_t)];
247 cbLeft -= cComponents * sizeof(uint32_t);
248 rc = VINF_SUCCESS;
249 }
250 else
251#endif
252 rc = RTAsn1MemAllocZ(&pThis->Allocation, (void **)&pThis->pauComponents, cComponents * sizeof(uint32_t));
253 if (RT_SUCCESS(rc))
254 {
255 /*
256 * Fill the elements array.
257 */
258 uint32_t *pauComponents = (uint32_t *)pThis->pauComponents;
259 rc = VINF_SUCCESS;
260 psz = pszObjId;
261 for (uint32_t i = 0; i < cComponents; i++)
262 {
263 uint32_t uValue = 0;
264 rc = RTStrToUInt32Ex(psz, (char **)&psz, 10, &uValue);
265 if (rc == VWRN_TRAILING_CHARS)
266 {
267 pauComponents[i] = uValue;
268 AssertBreakStmt(*psz == '.', rc = VERR_TRAILING_CHARS);
269 psz++;
270 }
271 else if (rc == VINF_SUCCESS)
272 {
273 pauComponents[i] = uValue;
274 Assert(*psz == '\0');
275 }
276 else if (RT_FAILURE(rc))
277 break;
278 else
279 {
280 rc = -rc;
281 break;
282 }
283 }
284 if (rc == VINF_SUCCESS && *psz == '\0')
285 {
286 /*
287 * Initialize the core structure before we start on the encoded bytes.
288 */
289 RTAsn1Core_InitEx(&pThis->Asn1Core,
290 ASN1_TAG_OID,
291 ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
292 &g_RTAsn1ObjId_Vtable,
293 RTASN1CORE_F_PRESENT | RTASN1CORE_F_PRIMITE_TAG_STRUCT);
294
295 /*
296 * Encode the value into the string buffer. This will NOT overflow
297 * because the string representation is much less efficient than the
298 * binary ASN.1 representation (base-10 + separators vs. base-128).
299 */
300 pThis->Asn1Core.cb = (uint32_t)cbObjId;
301 rc = rtAsn1ObjId_EncodeComponents(cComponents, pThis->pauComponents,
302 (uint8_t *)&pThis->szObjId[0], &pThis->Asn1Core.cb);
303 if (RT_SUCCESS(rc))
304 {
305 /*
306 * Now, find a place for the encoded bytes. There might be
307 * enough room left in the szObjId for it if we're lucky.
308 */
309#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? */
310 if (pThis->Asn1Core.cb >= cbLeft)
311 pThis->Asn1Core.uData.pv = memmove(&pThis->szObjId[cbObjId], &pThis->szObjId[0], pThis->Asn1Core.cb);
312 else
313#endif
314 rc = RTAsn1ContentDup(&pThis->Asn1Core, pThis->szObjId, pThis->Asn1Core.cb, pAllocator);
315 if (RT_SUCCESS(rc))
316 {
317 /*
318 * Finally, copy the dotted string.
319 */
320 memcpy(pThis->szObjId, pszObjId, cbObjId);
321 return VINF_SUCCESS;
322 }
323 }
324 else
325 {
326 AssertMsgFailed(("%Rrc\n", rc));
327 rc = VERR_ASN1_INTERNAL_ERROR_3;
328 }
329 }
330 else
331 rc = VERR_ASN1_OBJID_INVALID_DOTTED_STRING;
332 }
333 RT_ZERO(*pThis);
334 return rc;
335}
336
337
338RTDECL(int) RTAsn1ObjId_CompareWithString(PCRTASN1OBJID pThis, const char *pszRight)
339{
340 return strcmp(pThis->szObjId, pszRight);
341}
342
343
344RTDECL(bool) RTAsn1ObjId_StartsWith(PCRTASN1OBJID pThis, const char *pszStartsWith)
345{
346 size_t cchStartsWith = strlen(pszStartsWith);
347 return !strncmp(pThis->szObjId, pszStartsWith, cchStartsWith)
348 && ( pszStartsWith[cchStartsWith] == '.'
349 || pszStartsWith[cchStartsWith] == '\0');
350}
351
352
353RTDECL(uint8_t) RTAsn1ObjIdCountComponents(PCRTASN1OBJID pThis)
354{
355 return pThis->cComponents;
356}
357
358
359RTDECL(uint32_t) RTAsn1ObjIdGetComponentsAsUInt32(PCRTASN1OBJID pThis, uint8_t iComponent)
360{
361 if (iComponent < pThis->cComponents)
362 return pThis->pauComponents[iComponent];
363 return UINT32_MAX;
364}
365
366
367RTDECL(uint32_t) RTAsn1ObjIdGetLastComponentsAsUInt32(PCRTASN1OBJID pThis)
368{
369 return pThis->pauComponents[pThis->cComponents - 1];
370}
371
372
373/*
374 * ASN.1 OBJECT IDENTIFIER - Standard Methods.
375 */
376
377RT_DECL_DATA_CONST(RTASN1COREVTABLE const) g_RTAsn1ObjId_Vtable =
378{
379 "RTAsn1ObjId",
380 sizeof(RTASN1OBJID),
381 ASN1_TAG_OID,
382 ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
383 0,
384 (PFNRTASN1COREVTDTOR)RTAsn1ObjId_Delete,
385 NULL,
386 (PFNRTASN1COREVTCLONE)RTAsn1ObjId_Clone,
387 (PFNRTASN1COREVTCOMPARE)RTAsn1ObjId_Compare,
388 (PFNRTASN1COREVTCHECKSANITY)RTAsn1ObjId_CheckSanity,
389 NULL,
390 NULL
391};
392
393
394RTDECL(int) RTAsn1ObjId_Init(PRTASN1OBJID pThis, PCRTASN1ALLOCATORVTABLE pAllocator)
395{
396 RTAsn1Core_InitEx(&pThis->Asn1Core,
397 ASN1_TAG_OID,
398 ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
399 &g_RTAsn1ObjId_Vtable,
400 RTASN1CORE_F_PRESENT | RTASN1CORE_F_PRIMITE_TAG_STRUCT);
401 pThis->Asn1Core.cb = sizeof(g_abDefault);
402 pThis->Asn1Core.uData.pv = (void *)&g_abDefault[0];
403 pThis->cComponents = RT_ELEMENTS(g_auDefault);
404 pThis->pauComponents = g_auDefault;
405 AssertCompile(sizeof(g_szDefault) <= sizeof(pThis->szObjId));
406 memcpy(pThis->szObjId, g_szDefault, sizeof(g_szDefault));
407 return VINF_SUCCESS;
408}
409
410
411RTDECL(int) RTAsn1ObjId_Clone(PRTASN1OBJID pThis, PCRTASN1OBJID pSrc, PCRTASN1ALLOCATORVTABLE pAllocator)
412{
413 AssertPtr(pSrc); AssertPtr(pThis); AssertPtr(pAllocator);
414 RT_ZERO(*pThis);
415 if (RTAsn1ObjId_IsPresent(pSrc))
416 {
417 AssertReturn(pSrc->Asn1Core.pOps == &g_RTAsn1ObjId_Vtable, VERR_INTERNAL_ERROR_3);
418
419 /* Copy the dotted string representation. */
420 size_t cbObjId = strlen(pSrc->szObjId) + 1;
421 AssertReturn(cbObjId <= sizeof(pThis->szObjId), VERR_INTERNAL_ERROR_5);
422 memcpy(pThis->szObjId, pSrc->szObjId, cbObjId);
423
424 /* Copy the integer component array. Try fit it in the unused space of
425 the dotted object string buffer. We place it at the end of the
426 buffer as that is simple alignment wise and avoid wasting bytes that
427 could be used to sequueze in the content bytes (see below). */
428 int rc;
429 RTAsn1MemInitAllocation(&pThis->Allocation, pAllocator);
430 pThis->cComponents = pSrc->cComponents;
431 size_t cbLeft = sizeof(pThis->szObjId);
432#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? */
433 if (pSrc->cComponents * sizeof(uint32_t) <= cbLeft)
434 {
435 pThis->pauComponents = (uint32_t *)&pThis->szObjId[sizeof(pThis->szObjId) - pSrc->cComponents * sizeof(uint32_t)];
436 memcpy((uint32_t *)pThis->pauComponents, pSrc->pauComponents, pSrc->cComponents * sizeof(uint32_t));
437 cbLeft -= pSrc->cComponents * sizeof(uint32_t);
438 rc = VINF_SUCCESS;
439 }
440 else
441#endif
442 {
443 rc = RTAsn1MemDup(&pThis->Allocation, (void **)&pThis->pauComponents, pSrc->pauComponents,
444 pSrc->cComponents * sizeof(uint32_t));
445 }
446 if (RT_SUCCESS(rc))
447 {
448 /* See if we can fit the content value into the szObjId as well.
449 It will follow immediately after the string as the component
450 array is the end of the string buffer, when present. */
451#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? */
452 uint32_t cbContent = pSrc->Asn1Core.cb;
453 if (cbContent <= cbLeft)
454 {
455 rc = RTAsn1Core_CloneNoContent(&pThis->Asn1Core, &pSrc->Asn1Core);
456 if (RT_SUCCESS(rc))
457 {
458 pThis->Asn1Core.uData.pv = memcpy(&pThis->szObjId[cbObjId], pSrc->Asn1Core.uData.pv, cbContent);
459 return VINF_SUCCESS;
460 }
461 }
462 else
463#endif
464 {
465 rc = RTAsn1Core_CloneContent(&pThis->Asn1Core, &pSrc->Asn1Core, pAllocator);
466 if (RT_SUCCESS(rc))
467 return VINF_SUCCESS;
468 }
469 }
470
471 /* failed, clean up. */
472 if (pThis->Allocation.cbAllocated)
473 RTAsn1MemFree(&pThis->Allocation, (uint32_t *)pThis->pauComponents);
474 RT_ZERO(*pThis);
475 return rc;
476 }
477 return VINF_SUCCESS;
478}
479
480
481RTDECL(void) RTAsn1ObjId_Delete(PRTASN1OBJID pThis)
482{
483 if ( pThis
484 && RTAsn1ObjId_IsPresent(pThis))
485 {
486 Assert(pThis->Asn1Core.pOps == &g_RTAsn1ObjId_Vtable);
487
488 if (pThis->Allocation.cbAllocated)
489 RTAsn1MemFree(&pThis->Allocation, (uint32_t *)pThis->pauComponents);
490 RTAsn1ContentFree(&pThis->Asn1Core);
491 RT_ZERO(*pThis);
492 }
493}
494
495
496RTDECL(int) RTAsn1ObjId_Enum(PRTASN1OBJID pThis, PFNRTASN1ENUMCALLBACK pfnCallback, uint32_t uDepth, void *pvUser)
497{
498 Assert(pThis && (!RTAsn1ObjId_IsPresent(pThis) || pThis->Asn1Core.pOps == &g_RTAsn1ObjId_Vtable));
499
500 /* No children to enumerate. */
501 return VINF_SUCCESS;
502}
503
504
505RTDECL(int) RTAsn1ObjId_Compare(PCRTASN1OBJID pLeft, PCRTASN1OBJID pRight)
506{
507 if (RTAsn1ObjId_IsPresent(pLeft))
508 {
509 if (RTAsn1ObjId_IsPresent(pRight))
510 {
511 uint8_t cComponents = RT_MIN(pLeft->cComponents, pRight->cComponents);
512 for (uint32_t i = 0; i < cComponents; i++)
513 if (pLeft->pauComponents[i] != pRight->pauComponents[i])
514 return pLeft->pauComponents[i] < pRight->pauComponents[i] ? -1 : 1;
515
516 if (pLeft->cComponents == pRight->cComponents)
517 return 0;
518 return pLeft->cComponents < pRight->cComponents ? -1 : 1;
519 }
520 return 1;
521 }
522 return 0 - (int)RTAsn1ObjId_IsPresent(pRight);
523}
524
525
526RTDECL(int) RTAsn1ObjId_CheckSanity(PCRTASN1OBJID pThis, uint32_t fFlags, PRTERRINFO pErrInfo, const char *pszErrorTag)
527{
528 if (RT_UNLIKELY(!RTAsn1ObjId_IsPresent(pThis)))
529 return RTErrInfoSetF(pErrInfo, VERR_ASN1_NOT_PRESENT, "%s: Missing (OBJID).", pszErrorTag);
530 return VINF_SUCCESS;
531}
532
533
534/*
535 * Generate code for the associated collection types.
536 */
537#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-objid-template.h"
538#include <iprt/asn1-generator-internal-header.h>
539#include <iprt/asn1-generator-core.h>
540#include <iprt/asn1-generator-init.h>
541#include <iprt/asn1-generator-sanity.h>
542
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