VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
  • Property svn:mergeinfo set to (toggle deleted branches)
    /branches/VBox-3.0/src/VBox/Runtime/common/asn1/asn1-basics.cpp58652,​70973
    /branches/VBox-3.2/src/VBox/Runtime/common/asn1/asn1-basics.cpp66309,​66318
    /branches/VBox-4.0/src/VBox/Runtime/common/asn1/asn1-basics.cpp70873
    /branches/VBox-4.1/src/VBox/Runtime/common/asn1/asn1-basics.cpp74233,​78414,​78691,​81841,​82127,​85941,​85944-85947,​85949-85950,​85953,​86701,​86728,​87009
    /branches/VBox-4.2/src/VBox/Runtime/common/asn1/asn1-basics.cpp86229-86230,​86234,​86529,​91503-91504,​91506-91508,​91510,​91514-91515,​91521
    /branches/VBox-4.3/src/VBox/Runtime/common/asn1/asn1-basics.cpp91223
    /branches/VBox-4.3/trunk/src/VBox/Runtime/common/asn1/asn1-basics.cpp91223
    /branches/andy/draganddrop/src/VBox/Runtime/common/asn1/asn1-basics.cpp90781-91268
    /branches/andy/guestctrl20/src/VBox/Runtime/common/asn1/asn1-basics.cpp78916,​78930
    /branches/dsen/gui/src/VBox/Runtime/common/asn1/asn1-basics.cpp79076-79078,​79089,​79109-79110,​79112-79113,​79127-79130,​79134,​79141,​79151,​79155,​79157-79159,​79193,​79197
    /branches/dsen/gui2/src/VBox/Runtime/common/asn1/asn1-basics.cpp79224,​79228,​79233,​79235,​79258,​79262-79263,​79273,​79341,​79345,​79354,​79357,​79387-79388,​79559-79569,​79572-79573,​79578,​79581-79582,​79590-79591,​79598-79599,​79602-79603,​79605-79606,​79632,​79635,​79637,​79644
    /branches/dsen/gui3/src/VBox/Runtime/common/asn1/asn1-basics.cpp79645-79692
File size: 14.8 KB
Line 
1/* $Id: asn1-ut-objid-decode.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - ASN.1, OBJECT IDENTIFIER Type, Decoder.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include "internal/iprt.h"
42#include <iprt/asn1.h>
43
44#include <iprt/alloca.h>
45#include <iprt/err.h>
46#include <iprt/string.h>
47#include <iprt/ctype.h>
48
49#include <iprt/formats/asn1.h>
50
51
52/*********************************************************************************************************************************
53* Global Variables *
54*********************************************************************************************************************************/
55static char const g_achDigits[11] = "0123456789";
56
57
58/*********************************************************************************************************************************
59* Internal Functions *
60*********************************************************************************************************************************/
61DECLHIDDEN(int) rtAsn1ObjId_InternalFormatComponent(uint32_t uValue, char **ppszObjId, size_t *pcbObjId); /* asn1-ut-objid.cpp */
62
63
64/**
65 * Internal worker for RTAsn1ObjId_DecodeAsn1 that formats a component, with a
66 * leading dot.
67 *
68 * @returns VBox status code (caller complains on failure).
69 * @param uValue The component ID value.
70 * @param ppszObjId Pointer to the output buffer pointer.
71 * @param pcbObjId Pointer to the remaining size of the output buffer.
72 */
73DECLHIDDEN(int) rtAsn1ObjId_InternalFormatComponent(uint32_t uValue, char **ppszObjId, size_t *pcbObjId)
74{
75 /*
76 * Format the number backwards.
77 */
78 char szTmp[32];
79 char *psz = &szTmp[sizeof(szTmp) - 1];
80 *psz = '\0';
81
82 do
83 {
84 *--psz = g_achDigits[uValue % 10];
85 uValue /= 10;
86 } while (uValue > 0);
87
88 /*
89 * Do we have enough space?
90 * We add a dot and save space for the terminator.
91 */
92 size_t cchNeeded = &szTmp[sizeof(szTmp) - 1] - psz;
93 if (1 + cchNeeded < *pcbObjId)
94 {
95 *pcbObjId -= cchNeeded + 1;
96 char *pszObjId = *ppszObjId;
97 *ppszObjId = pszObjId + cchNeeded + 1;
98
99 *pszObjId = '.';
100 memcpy(pszObjId + 1, psz, cchNeeded);
101 return VINF_SUCCESS;
102 }
103
104 AssertFailed();
105 return VERR_ASN1_OBJID_TOO_LONG_STRING_FORM;
106}
107
108
109/**
110 * Reads one object ID component, returning it's value and encoded length.
111 *
112 * @returns The encoded length (positive) on success, negative IPRT status code
113 * on failure.
114 * @param pbContent The start of the component to parse.
115 * @param cbContent The number of content bytes left.
116 * @param puValue Where to return the value.
117 */
118static int rtAsn1ObjId_ReadComponent(uint8_t const *pbContent, uint32_t cbContent, uint32_t *puValue)
119{
120 if (cbContent >= 1)
121 {
122 /* The first byte. */
123 uint8_t b = *pbContent;
124 if (!(b & 0x80))
125 {
126 *puValue = b;
127 return 1;
128 }
129
130 /* Encoded as more than one byte. Make sure that it's efficently
131 encoded as 8.19.2 indicates it must. */
132 if (b != 0x80)
133 {
134 uint32_t off = 1;
135 uint32_t uValue = b & 0x7f;
136 while (off < cbContent)
137 {
138 b = pbContent[off++];
139 uValue <<= 7;
140 uValue |= b & 0x7f;
141 if (!(b & 0x80))
142 {
143 *puValue = uValue;
144 return (int)off;
145 }
146
147 if (RT_UNLIKELY(uValue & UINT32_C(0x0e000000)))
148 return VERR_ASN1_OBJID_COMPONENT_TOO_BIG;
149 }
150 }
151 return VERR_ASN1_INVALID_OBJID_ENCODING;
152 }
153 return VERR_NO_DATA;
154}
155
156
157/**
158 * This function parses the binary content of an OBJECT IDENTIFIER, check the
159 * encoding as well as calculating the storage requirements.
160 *
161 * @returns IPRT status code
162 * @param pbContent Pointer to the content.
163 * @param cbContent The length of the content.
164 * @param pCursor The cursor (for error reporting).
165 * @param pszErrorTag The error tag.
166 * @param pcComponents Where to return the component count.
167 * @param pcchObjId Where to return the length of the dotted string
168 * representation.
169 */
170static int rtAsn1ObjId_PreParse(uint8_t const *pbContent, uint32_t cbContent, PRTASN1CURSOR pCursor, const char *pszErrorTag,
171 uint8_t *pcComponents, uint8_t *pcchObjId)
172{
173 int rc;
174 if (cbContent >= 1 && cbContent < _1K)
175 {
176 /*
177 * Decode the first two numbers. Monkey business: X*40 + Y
178 * Where X is the first number, X in {0,1,2}, and Y is the second
179 * one. The range of Y is {0,...,39} for X in {0,1}, but has a
180 * free range for X = 2.
181 */
182 uint32_t cComponents = 1;
183 uint32_t uValue;
184 rc = rtAsn1ObjId_ReadComponent(pbContent, cbContent, &uValue);
185 if (rc > 0)
186 {
187 uint32_t cchObjId = 1;
188 uValue = uValue < 2*40 ? uValue % 40 : uValue - 2*40; /* Y */
189 do
190 {
191 cComponents++;
192
193 /* Figure the encoded string length, binary search fashion. */
194 if (uValue < 10000)
195 {
196 if (uValue < 100)
197 {
198 if (uValue < 10)
199 cchObjId += 1 + 1;
200 else
201 cchObjId += 1 + 2;
202 }
203 else
204 {
205 if (uValue < 1000)
206 cchObjId += 1 + 3;
207 else
208 cchObjId += 1 + 4;
209 }
210 }
211 else
212 {
213 if (uValue < 1000000)
214 {
215 if (uValue < 100000)
216 cchObjId += 1 + 5;
217 else
218 cchObjId += 1 + 6;
219 }
220 else
221 {
222 if (uValue < 10000000)
223 cchObjId += 1 + 7;
224 else if (uValue < 100000000)
225 cchObjId += 1 + 8;
226 else
227 cchObjId += 1 + 9;
228 }
229 }
230
231 /* advance. */
232 pbContent += rc;
233 cbContent -= rc;
234 if (!cbContent)
235 {
236 if (cComponents < 128)
237 {
238 if (cchObjId < RT_SIZEOFMEMB(RTASN1OBJID, szObjId))
239 {
240 *pcComponents = cComponents;
241 *pcchObjId = cchObjId;
242 return VINF_SUCCESS;
243 }
244 return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_OBJID_TOO_LONG_STRING_FORM,
245 "%s: Object ID has a too long string form: %#x (max %#x)",
246 pszErrorTag, cchObjId, RT_SIZEOFMEMB(RTASN1OBJID, szObjId));
247 }
248 return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_OBJID_TOO_MANY_COMPONENTS,
249 "%s: Object ID has too many components: %#x (max 127)", pszErrorTag, cComponents);
250 }
251
252 /* next */
253 rc = rtAsn1ObjId_ReadComponent(pbContent, cbContent, &uValue);
254 } while (rc > 0);
255 }
256 rc = RTAsn1CursorSetInfo(pCursor, rc, "%s: Bad object ID component #%u encoding: %.*Rhxs",
257 pszErrorTag, cComponents, cbContent, pbContent);
258 }
259 else if (cbContent)
260 rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_OBJID_ENCODING, "%s: Object ID content is loo long: %#x",
261 pszErrorTag, cbContent);
262 else
263 rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_OBJID_ENCODING, "%s: Zero length object ID content", pszErrorTag);
264 return rc;
265}
266
267
268
269RTDECL(int) RTAsn1ObjId_DecodeAsn1(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1OBJID pThis, const char *pszErrorTag)
270{
271 int rc = RTAsn1CursorReadHdr(pCursor, &pThis->Asn1Core, pszErrorTag);
272 if (RT_SUCCESS(rc))
273 {
274 rc = RTAsn1CursorMatchTagClassFlags(pCursor, &pThis->Asn1Core, ASN1_TAG_OID,
275 ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE, fFlags, pszErrorTag, "OID");
276 if (RT_SUCCESS(rc))
277 {
278 /*
279 * Validate and count things first.
280 */
281 uint8_t cComponents = 0; /* gcc maybe-crap */
282 uint8_t cchObjId = 0; /* ditto */
283 rc = rtAsn1ObjId_PreParse(pCursor->pbCur, pThis->Asn1Core.cb, pCursor, pszErrorTag, &cComponents, &cchObjId);
284 if (RT_SUCCESS(rc))
285 {
286 /*
287 * Allocate memory for the components array, either out of the
288 * string buffer or off the heap.
289 */
290 pThis->cComponents = cComponents;
291 RTAsn1CursorInitAllocation(pCursor, &pThis->Allocation);
292#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? */
293 if (cComponents * sizeof(uint32_t) <= sizeof(pThis->szObjId) - cchObjId - 1)
294 pThis->pauComponents = (uint32_t *)&pThis->szObjId[sizeof(pThis->szObjId) - cComponents * sizeof(uint32_t)];
295 else
296#endif
297 rc = RTAsn1MemAllocZ(&pThis->Allocation, (void **)&pThis->pauComponents,
298 cComponents * sizeof(pThis->pauComponents[0]));
299 if (RT_SUCCESS(rc))
300 {
301 uint32_t *pauComponents = (uint32_t *)pThis->pauComponents;
302
303 /*
304 * Deal with the two first components first since they are
305 * encoded in a weird way to save a byte.
306 */
307 uint8_t const *pbContent = pCursor->pbCur;
308 uint32_t cbContent = pThis->Asn1Core.cb;
309 uint32_t uValue;
310 rc = rtAsn1ObjId_ReadComponent(pbContent, cbContent, &uValue); AssertRC(rc);
311 if (RT_SUCCESS(rc))
312 {
313 pbContent += rc;
314 cbContent -= rc;
315
316 if (uValue < 80)
317 {
318 pauComponents[0] = uValue / 40;
319 pauComponents[1] = uValue % 40;
320 }
321 else
322 {
323 pauComponents[0] = 2;
324 pauComponents[1] = uValue - 2*40;
325 }
326
327 char *pszObjId = &pThis->szObjId[0];
328 *pszObjId++ = g_achDigits[pauComponents[0]];
329 size_t cbObjIdLeft = cchObjId + 1 - 1;
330
331 rc = rtAsn1ObjId_InternalFormatComponent(pauComponents[1], &pszObjId, &cbObjIdLeft); AssertRC(rc);
332 if (RT_SUCCESS(rc))
333 {
334 /*
335 * The other components are encoded in less complicated manner.
336 */
337 for (uint32_t i = 2; i < cComponents; i++)
338 {
339 rc = rtAsn1ObjId_ReadComponent(pbContent, cbContent, &uValue);
340 AssertRCBreak(rc);
341 pbContent += rc;
342 cbContent -= rc;
343 pauComponents[i] = uValue;
344 rc = rtAsn1ObjId_InternalFormatComponent(uValue, &pszObjId, &cbObjIdLeft);
345 AssertRCBreak(rc);
346 }
347 if (RT_SUCCESS(rc))
348 {
349 Assert(cbObjIdLeft == 1);
350 *pszObjId = '\0';
351
352 RTAsn1CursorSkip(pCursor, pThis->Asn1Core.cb);
353 pThis->Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
354 pThis->Asn1Core.pOps = &g_RTAsn1ObjId_Vtable;
355 return VINF_SUCCESS;
356 }
357 }
358 }
359 RTAsn1MemFree(&pThis->Allocation, (void *)pThis->pauComponents);
360 pThis->pauComponents = NULL;
361 }
362 }
363 }
364 }
365 RT_ZERO(*pThis);
366 return rc;
367}
368
369
370
371/*
372 * Generate code for the associated collection types.
373 */
374#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-objid-template.h"
375#include <iprt/asn1-generator-internal-header.h>
376#include <iprt/asn1-generator-asn1-decoder.h>
377
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use