VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/asn1/asn1-encode.cpp@ 53528

Last change on this file since 53528 was 51770, checked in by vboxsync, 11 years ago

Merged in iprt++ dev branch.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.4 KB
Line 
1/* $Id: asn1-encode.cpp 51770 2014-07-01 18:14:02Z vboxsync $ */
2/** @file
3 * IPRT - ASN.1, Encoding.
4 */
5
6/*
7 * Copyright (C) 2006-2014 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/assert.h>
34#include <iprt/bignum.h>
35#include <iprt/ctype.h>
36#include <iprt/err.h>
37
38#include <iprt/formats/asn1.h>
39
40
41/*******************************************************************************
42* Structures and Typedefs *
43*******************************************************************************/
44/**
45 * Argument package for rtAsn1EncodePrepareCallback passed by RTAsn1EncodePrepare.
46 */
47typedef struct RTASN1ENCODEPREPARGS
48{
49 /** The size at this level. */
50 uint32_t cb;
51 /** RTASN1ENCODE_F_XXX. */
52 uint32_t fFlags;
53 /** Pointer to the error info. (optional) */
54 PRTERRINFO pErrInfo;
55} RTASN1ENCODEPREPARGS;
56
57
58/**
59 * Argument package for rtAsn1EncodeWriteCallback passed by RTAsn1EncodeWrite.
60 */
61typedef struct RTASN1ENCODEWRITEARGS
62{
63 /** RTASN1ENCODE_F_XXX. */
64 uint32_t fFlags;
65 /** Pointer to the writer funtion. */
66 PFNRTASN1ENCODEWRITER pfnWriter;
67 /** User argument to the writer function. */
68 void *pvUser;
69 /** Pointer to the error info. (optional) */
70 PRTERRINFO pErrInfo;
71} RTASN1ENCODEWRITEARGS;
72
73
74RTDECL(int) RTAsn1EncodeRecalcHdrSize(PRTASN1CORE pAsn1Core, uint32_t fFlags, PRTERRINFO pErrInfo)
75{
76 AssertReturn((fFlags & RTASN1ENCODE_F_RULE_MASK) == RTASN1ENCODE_F_DER, VERR_INVALID_FLAGS);
77 int rc = VINF_SUCCESS;
78
79 uint8_t cbHdr;
80 if ((pAsn1Core->fFlags & (RTASN1CORE_F_PRESENT | RTASN1CORE_F_DUMMY | RTASN1CORE_F_DEFAULT)) == RTASN1CORE_F_PRESENT)
81 {
82 /*
83 * The minimum header size is two bytes.
84 */
85 cbHdr = 2;
86
87 /*
88 * Add additional bytes for encoding the tag.
89 */
90 uint32_t uTag = pAsn1Core->uTag;
91 if (uTag >= ASN1_TAG_USE_LONG_FORM)
92 {
93 AssertReturn(pAsn1Core->uTag != UINT32_MAX, RTErrInfoSet(pErrInfo, VERR_ASN1_DUMMY_OBJECT, "uTag=UINT32_MAX"));
94 do
95 {
96 cbHdr++;
97 uTag >>= 7;
98 } while (uTag > 0);
99 }
100
101 /*
102 * Add additional bytes for encoding the content length.
103 */
104 uint32_t cb = pAsn1Core->cb;
105 if (cb >= 0x80)
106 {
107 AssertReturn(cb < _1G, RTErrInfoSetF(pErrInfo, VERR_ASN1_TOO_LONG, "cb=%u (%#x)", cb, cb));
108
109 if (cb <= UINT32_C(0xffff))
110 {
111 if (cb <= UINT32_C(0xff))
112 cbHdr += 1;
113 else
114 cbHdr += 2;
115 }
116 else
117 {
118 if (cb <= UINT32_C(0xffffff))
119 cbHdr += 3;
120 else
121 cbHdr += 4;
122 }
123 }
124 }
125 /*
126 * Not present, dummy or otherwise not encoded.
127 */
128 else
129 {
130 cbHdr = 0;
131 if (pAsn1Core->fFlags & RTASN1CORE_F_DEFAULT)
132 rc = VINF_ASN1_NOT_ENCODED;
133 else
134 {
135 Assert(RTASN1CORE_IS_DUMMY(pAsn1Core));
136 Assert(pAsn1Core->pOps && pAsn1Core->pOps->pfnEnum);
137 rc = VINF_SUCCESS;
138 }
139 }
140
141 /*
142 * Update the header length.
143 */
144 pAsn1Core->cbHdr = cbHdr;
145 return rc;
146}
147
148
149/**
150 * @callback_method_impl{FNRTASN1ENUMCALLBACK}
151 */
152static DECLCALLBACK(int) rtAsn1EncodePrepareCallback(PRTASN1CORE pAsn1Core, const char *pszName, uint32_t uDepth, void *pvUser)
153{
154 RTASN1ENCODEPREPARGS *pArgs = (RTASN1ENCODEPREPARGS *)pvUser;
155 if (RTASN1CORE_IS_PRESENT(pAsn1Core))
156 {
157 /*
158 * Depth first, where relevant.
159 */
160 uint32_t const cbSaved = pArgs->cb;
161 if (pAsn1Core->pOps)
162 {
163 /*
164 * Use the encoding preparation method when available.
165 */
166 int rc;
167 if (pAsn1Core->pOps->pfnEncodePrep)
168 rc = pAsn1Core->pOps->pfnEncodePrep(pAsn1Core, pArgs->fFlags, pArgs->pErrInfo);
169 else if (pAsn1Core->pOps->pfnEnum)
170 {
171 /*
172 * Recurse to prepare the child objects (if any).
173 */
174 rc = pAsn1Core->pOps->pfnEnum(pAsn1Core, rtAsn1EncodePrepareCallback, uDepth + 1, pArgs);
175 if (RT_SUCCESS(rc))
176 pAsn1Core->cb = pArgs->cb - cbSaved;
177 }
178 else
179 {
180 /*
181 * Must be a primitive type if DER.
182 */
183 if ( (pAsn1Core->fClass & ASN1_TAGFLAG_CONSTRUCTED)
184 && (pArgs->fFlags & RTASN1ENCODE_F_DER) )
185 return RTErrInfoSetF(pArgs->pErrInfo, VERR_ASN1_EXPECTED_PRIMITIVE,
186 "Expected primitive ASN.1 object: uTag=%#x fClass=%#x cb=%u",
187 RTASN1CORE_GET_TAG(pAsn1Core), pAsn1Core->fClass, pAsn1Core->cb);
188 rc = VINF_SUCCESS;
189 }
190 if (RT_SUCCESS(rc))
191 rc = RTAsn1EncodeRecalcHdrSize(pAsn1Core, pArgs->fFlags, pArgs->pErrInfo);
192 if (RT_FAILURE(rc))
193 return rc;
194 }
195 else
196 {
197 AssertFailed();
198 pAsn1Core->cb = 0;
199 pAsn1Core->cbHdr = 0;
200 }
201
202 /*
203 * Recalculate the output size, thus far. Dummy objects propagates the
204 * content size, but the header size is zero. Other objects with
205 * header size zero are not encoded and should be omitted entirely.
206 */
207 if (pAsn1Core->cbHdr > 0 || RTASN1CORE_IS_DUMMY(pAsn1Core))
208 pArgs->cb = RTASN1CORE_GET_RAW_ASN1_SIZE(pAsn1Core) + cbSaved;
209 else
210 pArgs->cb = cbSaved;
211 }
212
213 return VINF_SUCCESS;
214}
215
216
217RTDECL(int) RTAsn1EncodePrepare(PRTASN1CORE pRoot, uint32_t fFlags, uint32_t *pcbEncoded, PRTERRINFO pErrInfo)
218{
219 AssertReturn((fFlags & RTASN1ENCODE_F_RULE_MASK) == RTASN1ENCODE_F_DER, VERR_INVALID_FLAGS);
220
221 /*
222 * This is implemented as a recursive enumeration of the ASN.1 object structure.
223 */
224 RTASN1ENCODEPREPARGS Args;
225 Args.cb = 0;
226 Args.fFlags = fFlags;
227 Args.pErrInfo = pErrInfo;
228 int rc = rtAsn1EncodePrepareCallback(pRoot, "root", 0, &Args);
229 if (pcbEncoded)
230 *pcbEncoded = RTASN1CORE_GET_RAW_ASN1_SIZE(pRoot);
231 return rc;
232}
233
234
235RTDECL(int) RTAsnEncodeWriteHeader(PCRTASN1CORE pAsn1Core, uint32_t fFlags, FNRTASN1ENCODEWRITER pfnWriter, void *pvUser,
236 PRTERRINFO pErrInfo)
237{
238 AssertReturn((fFlags & RTASN1ENCODE_F_RULE_MASK) == RTASN1ENCODE_F_DER, VERR_INVALID_FLAGS);
239
240 if ((pAsn1Core->fFlags & (RTASN1CORE_F_PRESENT | RTASN1CORE_F_DUMMY | RTASN1CORE_F_DEFAULT)) == RTASN1CORE_F_PRESENT)
241 {
242 uint8_t abHdr[16]; /* 2 + max 5 tag + max 4 length = 11 */
243 uint8_t *pbDst = &abHdr[0];
244
245 /*
246 * Encode the tag.
247 */
248 uint32_t uTag = pAsn1Core->uTag;
249 if (uTag < ASN1_TAG_USE_LONG_FORM)
250 *pbDst++ = (uint8_t)uTag | (pAsn1Core->fClass & ~ASN1_TAG_MASK);
251 else
252 {
253 AssertReturn(pAsn1Core->uTag != UINT32_MAX, RTErrInfoSet(pErrInfo, VERR_ASN1_DUMMY_OBJECT, "uTag=UINT32_MAX"));
254
255 /* In the long form, the tag is encoded MSB style with the 8th bit
256 of each byte indicating the whether there are more byte. */
257 *pbDst++ = ASN1_TAG_USE_LONG_FORM | (pAsn1Core->fClass & ~ASN1_TAG_MASK);
258 if (uTag <= UINT32_C(0x7f))
259 *pbDst++ = uTag;
260 else if (uTag <= UINT32_C(0x3fff)) /* 2**(7*2) = 0x4000 (16384) */
261 {
262 *pbDst++ = (uTag >> 7) | 0x80;
263 *pbDst++ = uTag & 0x7f;
264 }
265 else if (uTag <= UINT32_C(0x1fffff)) /* 2**(7*3) = 0x200000 (2097152) */
266 {
267 *pbDst++ = (uTag >> 14) | 0x80;
268 *pbDst++ = ((uTag >> 7) & 0x7f) | 0x80;
269 *pbDst++ = uTag & 0x7f;
270 }
271 else if (uTag <= UINT32_C(0xfffffff)) /* 2**(7*4) = 0x10000000 (268435456) */
272 {
273 *pbDst++ = (uTag >> 21) | 0x80;
274 *pbDst++ = ((uTag >> 14) & 0x7f) | 0x80;
275 *pbDst++ = ((uTag >> 7) & 0x7f) | 0x80;
276 *pbDst++ = uTag & 0x7f;
277 }
278 else
279 {
280 *pbDst++ = (uTag >> 28) | 0x80;
281 *pbDst++ = ((uTag >> 21) & 0x7f) | 0x80;
282 *pbDst++ = ((uTag >> 14) & 0x7f) | 0x80;
283 *pbDst++ = ((uTag >> 7) & 0x7f) | 0x80;
284 *pbDst++ = uTag & 0x7f;
285 }
286 }
287
288 /*
289 * Encode the length.
290 */
291 uint32_t cb = pAsn1Core->cb;
292 if (cb < 0x80)
293 *pbDst++ = (uint8_t)cb;
294 else
295 {
296 AssertReturn(cb < _1G, RTErrInfoSetF(pErrInfo, VERR_ASN1_TOO_LONG, "cb=%u (%#x)", cb, cb));
297
298 if (cb <= UINT32_C(0xffff))
299 {
300 if (cb <= UINT32_C(0xff))
301 {
302 pbDst[0] = 0x81;
303 pbDst[1] = (uint8_t)cb;
304 pbDst += 2;
305 }
306 else
307 {
308 pbDst[0] = 0x82;
309 pbDst[1] = cb >> 8;
310 pbDst[2] = (uint8_t)cb;
311 pbDst += 3;
312 }
313 }
314 else
315 {
316 if (cb <= UINT32_C(0xffffff))
317 {
318 pbDst[0] = 0x83;
319 pbDst[1] = (uint8_t)(cb >> 16);
320 pbDst[2] = (uint8_t)(cb >> 8);
321 pbDst[3] = (uint8_t)cb;
322 pbDst += 4;
323 }
324 else
325 {
326 pbDst[0] = 0x84;
327 pbDst[1] = (uint8_t)(cb >> 24);
328 pbDst[2] = (uint8_t)(cb >> 16);
329 pbDst[3] = (uint8_t)(cb >> 8);
330 pbDst[4] = (uint8_t)cb;
331 pbDst += 5;
332 }
333 }
334 }
335
336 size_t const cbHdr = pbDst - &abHdr[0];
337 Assert(sizeof(abHdr) >= cbHdr);
338 Assert(pAsn1Core->cbHdr == cbHdr);
339
340 /*
341 * Write it.
342 */
343 return pfnWriter(abHdr, cbHdr, pvUser, pErrInfo);
344 }
345
346 /*
347 * Not present, dummy or otherwise not encoded.
348 */
349 Assert(pAsn1Core->cbHdr == 0);
350 if (pAsn1Core->fFlags & RTASN1CORE_F_DEFAULT)
351 return VINF_ASN1_NOT_ENCODED;
352 Assert(RTASN1CORE_IS_DUMMY(pAsn1Core));
353 Assert(pAsn1Core->pOps && pAsn1Core->pOps->pfnEnum);
354 return VINF_SUCCESS;
355}
356
357
358/**
359 * @callback_method_impl{FNRTASN1ENUMCALLBACK}
360 */
361static DECLCALLBACK(int) rtAsn1EncodeWriteCallback(PRTASN1CORE pAsn1Core, const char *pszName, uint32_t uDepth, void *pvUser)
362{
363 RTASN1ENCODEWRITEARGS *pArgs = (RTASN1ENCODEWRITEARGS *)pvUser;
364 int rc;
365 if (RTASN1CORE_IS_PRESENT(pAsn1Core))
366 {
367 /*
368 * If there is an write method, use it.
369 */
370 if ( pAsn1Core->pOps
371 && pAsn1Core->pOps->pfnEncodeWrite)
372 rc = pAsn1Core->pOps->pfnEncodeWrite(pAsn1Core, pArgs->fFlags, pArgs->pfnWriter, pArgs->pvUser, pArgs->pErrInfo);
373 else
374 {
375 /*
376 * Generic path. Start by writing the header for this object.
377 */
378 rc = RTAsnEncodeWriteHeader(pAsn1Core, pArgs->fFlags, pArgs->pfnWriter, pArgs->pvUser, pArgs->pErrInfo);
379 if (RT_SUCCESS(rc))
380 {
381 /*
382 * If there is an enum function, call it to assemble the content.
383 * Otherwise ASSUME the pointer in the header points to the content.
384 */
385 if ( pAsn1Core->pOps
386 && pAsn1Core->pOps->pfnEnum)
387 {
388 if (rc != VINF_ASN1_NOT_ENCODED)
389 rc = pAsn1Core->pOps->pfnEnum(pAsn1Core, rtAsn1EncodeWriteCallback, uDepth + 1, pArgs);
390 }
391 else if (pAsn1Core->cb && rc != VINF_ASN1_NOT_ENCODED)
392 {
393 Assert(!RTASN1CORE_IS_DUMMY(pAsn1Core));
394 AssertPtrReturn(pAsn1Core->uData.pv,
395 RTErrInfoSetF(pArgs->pErrInfo, VERR_ASN1_INVALID_DATA_POINTER,
396 "Invalid uData pointer %p for no pfnEnum object with %#x bytes of content",
397 pAsn1Core->uData.pv, pAsn1Core->cb));
398 rc = pArgs->pfnWriter(pAsn1Core->uData.pv, pAsn1Core->cb, pArgs->pvUser, pArgs->pErrInfo);
399 }
400 }
401 }
402 if (RT_SUCCESS(rc))
403 rc = VINF_SUCCESS;
404 }
405 else
406 rc = VINF_SUCCESS;
407 return rc;
408}
409
410
411RTDECL(int) RTAsn1EncodeWrite(PCRTASN1CORE pRoot, uint32_t fFlags, FNRTASN1ENCODEWRITER pfnWriter, void *pvUser,
412 PRTERRINFO pErrInfo)
413{
414 AssertReturn((fFlags & RTASN1ENCODE_F_RULE_MASK) == RTASN1ENCODE_F_DER, VERR_INVALID_FLAGS);
415
416 /*
417 * This is implemented as a recursive enumeration of the ASN.1 object structure.
418 */
419 RTASN1ENCODEWRITEARGS Args;
420 Args.fFlags = fFlags;
421 Args.pfnWriter = pfnWriter;
422 Args.pvUser = pvUser;
423 Args.pErrInfo = pErrInfo;
424 return rtAsn1EncodeWriteCallback((PRTASN1CORE)pRoot, "root", 0, &Args);
425}
426
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