VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/asn1/asn1-encode.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
File size: 17.8 KB
Line 
1/* $Id: asn1-encode.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - ASN.1, Encoding.
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/assert.h>
45#include <iprt/bignum.h>
46#include <iprt/ctype.h>
47#include <iprt/err.h>
48#include <iprt/mem.h>
49#include <iprt/string.h>
50
51#include <iprt/formats/asn1.h>
52
53
54/*********************************************************************************************************************************
55* Structures and Typedefs *
56*********************************************************************************************************************************/
57/**
58 * Argument package for rtAsn1EncodePrepareCallback passed by RTAsn1EncodePrepare.
59 */
60typedef struct RTASN1ENCODEPREPARGS
61{
62 /** The size at this level. */
63 uint32_t cb;
64 /** RTASN1ENCODE_F_XXX. */
65 uint32_t fFlags;
66 /** Pointer to the error info. (optional) */
67 PRTERRINFO pErrInfo;
68} RTASN1ENCODEPREPARGS;
69
70
71/**
72 * Argument package for rtAsn1EncodeWriteCallback passed by RTAsn1EncodeWrite.
73 */
74typedef struct RTASN1ENCODEWRITEARGS
75{
76 /** RTASN1ENCODE_F_XXX. */
77 uint32_t fFlags;
78 /** Pointer to the writer funtion. */
79 PFNRTASN1ENCODEWRITER pfnWriter;
80 /** User argument to the writer function. */
81 void *pvUser;
82 /** Pointer to the error info. (optional) */
83 PRTERRINFO pErrInfo;
84} RTASN1ENCODEWRITEARGS;
85
86/**
87 * Argument package for rtAsn1EncodeToBufferCallback passed by
88 * RTAsn1EncodeToBuffer.
89 */
90typedef struct RTASN1ENCODETOBUFARGS
91{
92 /** The destination buffer position (incremented while writing). */
93 uint8_t *pbDst;
94 /** The size of the destination buffer left (decremented while writing). */
95 size_t cbDst;
96} RTASN1ENCODETOBUFARGS;
97
98
99RTDECL(int) RTAsn1EncodeRecalcHdrSize(PRTASN1CORE pAsn1Core, uint32_t fFlags, PRTERRINFO pErrInfo)
100{
101 AssertReturn((fFlags & RTASN1ENCODE_F_RULE_MASK) == RTASN1ENCODE_F_DER, VERR_INVALID_FLAGS);
102 int rc = VINF_SUCCESS;
103
104 uint8_t cbHdr;
105 if ((pAsn1Core->fFlags & (RTASN1CORE_F_PRESENT | RTASN1CORE_F_DUMMY | RTASN1CORE_F_DEFAULT)) == RTASN1CORE_F_PRESENT)
106 {
107 /*
108 * The minimum header size is two bytes.
109 */
110 cbHdr = 2;
111
112 /*
113 * Add additional bytes for encoding the tag.
114 */
115 uint32_t uTag = pAsn1Core->uTag;
116 if (uTag >= ASN1_TAG_USE_LONG_FORM)
117 {
118 AssertReturn(pAsn1Core->uTag != UINT32_MAX, RTErrInfoSet(pErrInfo, VERR_ASN1_DUMMY_OBJECT, "uTag=UINT32_MAX"));
119 do
120 {
121 cbHdr++;
122 uTag >>= 7;
123 } while (uTag > 0);
124 }
125
126 /*
127 * Add additional bytes for encoding the content length.
128 */
129 uint32_t cb = pAsn1Core->cb;
130 if (cb >= 0x80)
131 {
132 AssertReturn(cb < _1G, RTErrInfoSetF(pErrInfo, VERR_ASN1_TOO_LONG, "cb=%u (%#x)", cb, cb));
133
134 if (cb <= UINT32_C(0xffff))
135 {
136 if (cb <= UINT32_C(0xff))
137 cbHdr += 1;
138 else
139 cbHdr += 2;
140 }
141 else
142 {
143 if (cb <= UINT32_C(0xffffff))
144 cbHdr += 3;
145 else
146 cbHdr += 4;
147 }
148 }
149 }
150 /*
151 * Not present, dummy or otherwise not encoded.
152 */
153 else
154 {
155 cbHdr = 0;
156 if (pAsn1Core->fFlags & RTASN1CORE_F_DEFAULT)
157 rc = VINF_ASN1_NOT_ENCODED;
158 else
159 {
160 Assert(RTASN1CORE_IS_DUMMY(pAsn1Core));
161 Assert(pAsn1Core->pOps && pAsn1Core->pOps->pfnEnum);
162 rc = VINF_SUCCESS;
163 }
164 }
165
166 /*
167 * Update the header length.
168 */
169 pAsn1Core->cbHdr = cbHdr;
170 return rc;
171}
172
173
174/**
175 * @callback_method_impl{FNRTASN1ENUMCALLBACK}
176 */
177static DECLCALLBACK(int) rtAsn1EncodePrepareCallback(PRTASN1CORE pAsn1Core, const char *pszName, uint32_t uDepth, void *pvUser)
178{
179 RTASN1ENCODEPREPARGS *pArgs = (RTASN1ENCODEPREPARGS *)pvUser;
180 RT_NOREF_PV(pszName);
181 if (RTASN1CORE_IS_PRESENT(pAsn1Core))
182 {
183 /*
184 * Depth first, where relevant.
185 */
186 uint32_t const cbSaved = pArgs->cb;
187 if (pAsn1Core->pOps)
188 {
189 /*
190 * Use the encoding preparation method when available.
191 */
192 int rc;
193 if (pAsn1Core->pOps->pfnEncodePrep)
194 rc = pAsn1Core->pOps->pfnEncodePrep(pAsn1Core, pArgs->fFlags, pArgs->pErrInfo);
195 else if (pAsn1Core->pOps->pfnEnum)
196 {
197 /*
198 * Recurse to prepare the child objects (if any).
199 */
200 rc = pAsn1Core->pOps->pfnEnum(pAsn1Core, rtAsn1EncodePrepareCallback, uDepth + 1, pArgs);
201 if (RT_SUCCESS(rc))
202 pAsn1Core->cb = pArgs->cb - cbSaved;
203 }
204 else
205 {
206 /*
207 * Must be a primitive type if DER.
208 */
209 if ( (pAsn1Core->fClass & ASN1_TAGFLAG_CONSTRUCTED)
210 && (pArgs->fFlags & RTASN1ENCODE_F_DER) )
211 return RTErrInfoSetF(pArgs->pErrInfo, VERR_ASN1_EXPECTED_PRIMITIVE,
212 "Expected primitive ASN.1 object: uTag=%#x fClass=%#x cb=%u",
213 RTASN1CORE_GET_TAG(pAsn1Core), pAsn1Core->fClass, pAsn1Core->cb);
214 rc = VINF_SUCCESS;
215 }
216 if (RT_SUCCESS(rc))
217 rc = RTAsn1EncodeRecalcHdrSize(pAsn1Core, pArgs->fFlags, pArgs->pErrInfo);
218 if (RT_FAILURE(rc))
219 return rc;
220 }
221 else
222 {
223 AssertFailed();
224 pAsn1Core->cb = 0;
225 pAsn1Core->cbHdr = 0;
226 }
227
228 /*
229 * Recalculate the output size, thus far. Dummy objects propagates the
230 * content size, but the header size is zero. Other objects with
231 * header size zero are not encoded and should be omitted entirely.
232 */
233 if (pAsn1Core->cbHdr > 0 || RTASN1CORE_IS_DUMMY(pAsn1Core))
234 pArgs->cb = RTASN1CORE_GET_RAW_ASN1_SIZE(pAsn1Core) + cbSaved;
235 else
236 pArgs->cb = cbSaved;
237 }
238
239 return VINF_SUCCESS;
240}
241
242
243RTDECL(int) RTAsn1EncodePrepare(PRTASN1CORE pRoot, uint32_t fFlags, uint32_t *pcbEncoded, PRTERRINFO pErrInfo)
244{
245 AssertReturn((fFlags & RTASN1ENCODE_F_RULE_MASK) == RTASN1ENCODE_F_DER, VERR_INVALID_FLAGS);
246
247 /*
248 * This is implemented as a recursive enumeration of the ASN.1 object structure.
249 */
250 RTASN1ENCODEPREPARGS Args;
251 Args.cb = 0;
252 Args.fFlags = fFlags;
253 Args.pErrInfo = pErrInfo;
254 int rc = rtAsn1EncodePrepareCallback(pRoot, "root", 0, &Args);
255 if (pcbEncoded)
256 *pcbEncoded = RTASN1CORE_GET_RAW_ASN1_SIZE(pRoot);
257 return rc;
258}
259
260
261RTDECL(int) RTAsn1EncodeWriteHeader(PCRTASN1CORE pAsn1Core, uint32_t fFlags, FNRTASN1ENCODEWRITER pfnWriter, void *pvUser,
262 PRTERRINFO pErrInfo)
263{
264 AssertReturn((fFlags & RTASN1ENCODE_F_RULE_MASK) == RTASN1ENCODE_F_DER, VERR_INVALID_FLAGS);
265
266 if ((pAsn1Core->fFlags & (RTASN1CORE_F_PRESENT | RTASN1CORE_F_DUMMY | RTASN1CORE_F_DEFAULT)) == RTASN1CORE_F_PRESENT)
267 {
268 uint8_t abHdr[16]; /* 2 + max 5 tag + max 4 length = 11 */
269 uint8_t *pbDst = &abHdr[0];
270
271 /*
272 * Encode the tag.
273 */
274 uint32_t uTag = pAsn1Core->uTag;
275 if (uTag < ASN1_TAG_USE_LONG_FORM)
276 *pbDst++ = (uint8_t)uTag | (pAsn1Core->fClass & ~ASN1_TAG_MASK);
277 else
278 {
279 AssertReturn(pAsn1Core->uTag != UINT32_MAX, RTErrInfoSet(pErrInfo, VERR_ASN1_DUMMY_OBJECT, "uTag=UINT32_MAX"));
280
281 /* In the long form, the tag is encoded MSB style with the 8th bit
282 of each byte indicating the whether there are more byte. */
283 *pbDst++ = ASN1_TAG_USE_LONG_FORM | (pAsn1Core->fClass & ~ASN1_TAG_MASK);
284 if (uTag <= UINT32_C(0x7f))
285 *pbDst++ = uTag;
286 else if (uTag <= UINT32_C(0x3fff)) /* 2**(7*2) = 0x4000 (16384) */
287 {
288 *pbDst++ = (uTag >> 7) | 0x80;
289 *pbDst++ = uTag & 0x7f;
290 }
291 else if (uTag <= UINT32_C(0x1fffff)) /* 2**(7*3) = 0x200000 (2097152) */
292 {
293 *pbDst++ = (uTag >> 14) | 0x80;
294 *pbDst++ = ((uTag >> 7) & 0x7f) | 0x80;
295 *pbDst++ = uTag & 0x7f;
296 }
297 else if (uTag <= UINT32_C(0xfffffff)) /* 2**(7*4) = 0x10000000 (268435456) */
298 {
299 *pbDst++ = (uTag >> 21) | 0x80;
300 *pbDst++ = ((uTag >> 14) & 0x7f) | 0x80;
301 *pbDst++ = ((uTag >> 7) & 0x7f) | 0x80;
302 *pbDst++ = uTag & 0x7f;
303 }
304 else
305 {
306 *pbDst++ = (uTag >> 28) | 0x80;
307 *pbDst++ = ((uTag >> 21) & 0x7f) | 0x80;
308 *pbDst++ = ((uTag >> 14) & 0x7f) | 0x80;
309 *pbDst++ = ((uTag >> 7) & 0x7f) | 0x80;
310 *pbDst++ = uTag & 0x7f;
311 }
312 }
313
314 /*
315 * Encode the length.
316 */
317 uint32_t cb = pAsn1Core->cb;
318 if (cb < 0x80)
319 *pbDst++ = (uint8_t)cb;
320 else
321 {
322 AssertReturn(cb < _1G, RTErrInfoSetF(pErrInfo, VERR_ASN1_TOO_LONG, "cb=%u (%#x)", cb, cb));
323
324 if (cb <= UINT32_C(0xffff))
325 {
326 if (cb <= UINT32_C(0xff))
327 {
328 pbDst[0] = 0x81;
329 pbDst[1] = (uint8_t)cb;
330 pbDst += 2;
331 }
332 else
333 {
334 pbDst[0] = 0x82;
335 pbDst[1] = cb >> 8;
336 pbDst[2] = (uint8_t)cb;
337 pbDst += 3;
338 }
339 }
340 else
341 {
342 if (cb <= UINT32_C(0xffffff))
343 {
344 pbDst[0] = 0x83;
345 pbDst[1] = (uint8_t)(cb >> 16);
346 pbDst[2] = (uint8_t)(cb >> 8);
347 pbDst[3] = (uint8_t)cb;
348 pbDst += 4;
349 }
350 else
351 {
352 pbDst[0] = 0x84;
353 pbDst[1] = (uint8_t)(cb >> 24);
354 pbDst[2] = (uint8_t)(cb >> 16);
355 pbDst[3] = (uint8_t)(cb >> 8);
356 pbDst[4] = (uint8_t)cb;
357 pbDst += 5;
358 }
359 }
360 }
361
362 size_t const cbHdr = pbDst - &abHdr[0];
363 Assert(sizeof(abHdr) >= cbHdr);
364 Assert(pAsn1Core->cbHdr == cbHdr);
365
366 /*
367 * Write it.
368 */
369 return pfnWriter(abHdr, cbHdr, pvUser, pErrInfo);
370 }
371
372 /*
373 * Not present, dummy or otherwise not encoded.
374 */
375 Assert(pAsn1Core->cbHdr == 0);
376 if (pAsn1Core->fFlags & RTASN1CORE_F_DEFAULT)
377 return VINF_ASN1_NOT_ENCODED;
378 Assert(RTASN1CORE_IS_DUMMY(pAsn1Core));
379 Assert(pAsn1Core->pOps && pAsn1Core->pOps->pfnEnum);
380 return VINF_SUCCESS;
381}
382
383
384/**
385 * @callback_method_impl{FNRTASN1ENUMCALLBACK}
386 */
387static DECLCALLBACK(int) rtAsn1EncodeWriteCallback(PRTASN1CORE pAsn1Core, const char *pszName, uint32_t uDepth, void *pvUser)
388{
389 RTASN1ENCODEWRITEARGS *pArgs = (RTASN1ENCODEWRITEARGS *)pvUser;
390 RT_NOREF_PV(pszName);
391 int rc;
392 if (RTASN1CORE_IS_PRESENT(pAsn1Core))
393 {
394 /*
395 * If there is an write method, use it.
396 */
397 if ( pAsn1Core->pOps
398 && pAsn1Core->pOps->pfnEncodeWrite)
399 rc = pAsn1Core->pOps->pfnEncodeWrite(pAsn1Core, pArgs->fFlags, pArgs->pfnWriter, pArgs->pvUser, pArgs->pErrInfo);
400 else
401 {
402 /*
403 * Generic path. Start by writing the header for this object.
404 */
405 rc = RTAsn1EncodeWriteHeader(pAsn1Core, pArgs->fFlags, pArgs->pfnWriter, pArgs->pvUser, pArgs->pErrInfo);
406 if (RT_SUCCESS(rc))
407 {
408 /*
409 * If there is an enum function, call it to assemble the content.
410 * Otherwise ASSUME the pointer in the header points to the content.
411 */
412 if ( pAsn1Core->pOps
413 && pAsn1Core->pOps->pfnEnum)
414 {
415 if (rc != VINF_ASN1_NOT_ENCODED)
416 rc = pAsn1Core->pOps->pfnEnum(pAsn1Core, rtAsn1EncodeWriteCallback, uDepth + 1, pArgs);
417 }
418 else if (pAsn1Core->cb && rc != VINF_ASN1_NOT_ENCODED)
419 {
420 Assert(!RTASN1CORE_IS_DUMMY(pAsn1Core));
421 AssertPtrReturn(pAsn1Core->uData.pv,
422 RTErrInfoSetF(pArgs->pErrInfo, VERR_ASN1_INVALID_DATA_POINTER,
423 "Invalid uData pointer %p for no pfnEnum object with %#x bytes of content",
424 pAsn1Core->uData.pv, pAsn1Core->cb));
425 rc = pArgs->pfnWriter(pAsn1Core->uData.pv, pAsn1Core->cb, pArgs->pvUser, pArgs->pErrInfo);
426 }
427 }
428 }
429 if (RT_SUCCESS(rc))
430 rc = VINF_SUCCESS;
431 }
432 else
433 rc = VINF_SUCCESS;
434 return rc;
435}
436
437
438RTDECL(int) RTAsn1EncodeWrite(PCRTASN1CORE pRoot, uint32_t fFlags, FNRTASN1ENCODEWRITER pfnWriter, void *pvUser,
439 PRTERRINFO pErrInfo)
440{
441 AssertReturn((fFlags & RTASN1ENCODE_F_RULE_MASK) == RTASN1ENCODE_F_DER, VERR_INVALID_FLAGS);
442
443 /*
444 * This is implemented as a recursive enumeration of the ASN.1 object structure.
445 */
446 RTASN1ENCODEWRITEARGS Args;
447 Args.fFlags = fFlags;
448 Args.pfnWriter = pfnWriter;
449 Args.pvUser = pvUser;
450 Args.pErrInfo = pErrInfo;
451 return rtAsn1EncodeWriteCallback((PRTASN1CORE)pRoot, "root", 0, &Args);
452}
453
454
455static DECLCALLBACK(int) rtAsn1EncodeToBufferCallback(const void *pvBuf, size_t cbToWrite, void *pvUser, PRTERRINFO pErrInfo)
456{
457 RTASN1ENCODETOBUFARGS *pArgs = (RTASN1ENCODETOBUFARGS *)pvUser;
458 if (RT_LIKELY(pArgs->cbDst >= cbToWrite))
459 {
460 memcpy(pArgs->pbDst, pvBuf, cbToWrite);
461 pArgs->cbDst -= cbToWrite;
462 pArgs->pbDst += cbToWrite;
463 return VINF_SUCCESS;
464 }
465
466 /*
467 * Overflow.
468 */
469 if (pArgs->cbDst)
470 {
471 memcpy(pArgs->pbDst, pvBuf, pArgs->cbDst);
472 pArgs->pbDst -= pArgs->cbDst;
473 pArgs->cbDst = 0;
474 }
475 RT_NOREF_PV(pErrInfo);
476 return VERR_BUFFER_OVERFLOW;
477}
478
479
480RTDECL(int) RTAsn1EncodeToBuffer(PCRTASN1CORE pRoot, uint32_t fFlags, void *pvBuf, size_t cbBuf, PRTERRINFO pErrInfo)
481{
482 RTASN1ENCODETOBUFARGS Args;
483 Args.pbDst = (uint8_t *)pvBuf;
484 Args.cbDst = cbBuf;
485 return RTAsn1EncodeWrite(pRoot, fFlags, rtAsn1EncodeToBufferCallback, &Args, pErrInfo);
486}
487
488
489RTDECL(int) RTAsn1EncodeQueryRawBits(PRTASN1CORE pRoot, const uint8_t **ppbRaw, uint32_t *pcbRaw,
490 void **ppvFree, PRTERRINFO pErrInfo)
491{
492 /*
493 * ASSUME that if we've got pointers here, they are valid...
494 */
495 if ( pRoot->uData.pv
496 && !(pRoot->fFlags & RTASN1CORE_F_INDEFINITE_LENGTH) /* BER, not DER. */
497 && (pRoot->fFlags & RTASN1CORE_F_DECODED_CONTENT) )
498 {
499 /** @todo Check that it's DER encoding. */
500 *ppbRaw = RTASN1CORE_GET_RAW_ASN1_PTR(pRoot);
501 *pcbRaw = RTASN1CORE_GET_RAW_ASN1_SIZE(pRoot);
502 *ppvFree = NULL;
503 return VINF_SUCCESS;
504 }
505
506 /*
507 * Encode it into a temporary heap buffer.
508 */
509 uint32_t cbEncoded = 0;
510 int rc = RTAsn1EncodePrepare(pRoot, RTASN1ENCODE_F_DER, &cbEncoded, pErrInfo);
511 if (RT_SUCCESS(rc))
512 {
513 void *pvEncoded = RTMemTmpAllocZ(cbEncoded);
514 if (pvEncoded)
515 {
516 rc = RTAsn1EncodeToBuffer(pRoot, RTASN1ENCODE_F_DER, pvEncoded, cbEncoded, pErrInfo);
517 if (RT_SUCCESS(rc))
518 {
519 *ppvFree = pvEncoded;
520 *ppbRaw = (unsigned char *)pvEncoded;
521 *pcbRaw = cbEncoded;
522 return VINF_SUCCESS;
523 }
524 RTMemTmpFree(pvEncoded);
525 }
526 else
527 rc = RTErrInfoSetF(pErrInfo, VERR_NO_TMP_MEMORY, "RTMemTmpAllocZ(%u)", cbEncoded);
528 }
529
530 *ppvFree = NULL;
531 *ppbRaw = NULL;
532 *pcbRaw = 0;
533 return rc;
534}
535
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use