VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/asn1/asn1-ut-time-decode.cpp@ 52600

Last change on this file since 52600 was 52600, checked in by vboxsync, 10 years ago

IPRT: Added support for microsoft timestamp counter signatures. This required making the PKCS #7 code accept some of the CMS (RFC-5652) stuff.

  • 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: 16.8 KB
Line 
1/* $Id: asn1-ut-time-decode.cpp 52600 2014-09-04 22:59:00Z vboxsync $ */
2/** @file
3 * IPRT - ASN.1, UTC TIME and GENERALIZED TIME Types, Decoding.
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/alloca.h>
34#include <iprt/err.h>
35#include <iprt/string.h>
36#include <iprt/ctype.h>
37
38#include <iprt/formats/asn1.h>
39
40
41/**
42 * Common code for UTCTime and GeneralizedTime converters that normalizes the
43 * converted time and checks that the input values doesn't change.
44 *
45 * @returns IPRT status code.
46 * @param pCursor The cursor to use when reporting an error.
47 * @param pThis The time to normalize and check.
48 * @param pszType The type name.
49 * @param pszErrorTag The error tag.
50 */
51static int rtAsn1Time_NormalizeTime(PRTASN1CURSOR pCursor, PRTASN1TIME pThis, const char *pszType, const char *pszErrorTag)
52{
53 int rc;
54 if ( pThis->Time.u8Month > 0
55 && pThis->Time.u8Month <= 12
56 && pThis->Time.u8Hour < 24
57 && pThis->Time.u8Minute < 60
58 && pThis->Time.u8Second < 60) /** @todo what about leap seconds? */
59 {
60 RTTIME const TimeCopy = pThis->Time;
61 if (RTTimeNormalize(&pThis->Time))
62 {
63 if ( TimeCopy.u8MonthDay == pThis->Time.u8MonthDay
64 && TimeCopy.u8Month == pThis->Time.u8Month
65 && TimeCopy.i32Year == pThis->Time.i32Year
66 && TimeCopy.u8Hour == pThis->Time.u8Hour
67 && TimeCopy.u8Minute == pThis->Time.u8Minute
68 && TimeCopy.u8Second == pThis->Time.u8Second)
69 return VINF_SUCCESS;
70
71 rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_TIME_NORMALIZE_MISMATCH,
72 "%s: Normalized result not the same as %s: '%.*s'",
73 pszErrorTag, pszType, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
74 }
75 else
76 rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_TIME_NORMALIZE_ERROR,
77 "%s: RTTimeNormalize failed on %s: '%.*s'",
78 pszErrorTag, pszType, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
79 }
80 else
81 rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_TIME_BAD_NORMALIZE_INPUT,
82 "%s: Bad %s values: '%.*s'; mth=%u h=%u min=%u sec=%u",
83 pszErrorTag, pszType, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch,
84 pThis->Time.u8Month, pThis->Time.u8Hour, pThis->Time.u8Minute, pThis->Time.u8Second);
85 return rc;
86}
87
88
89/**
90 * Converts the UTCTime string into an the RTTIME member of RTASN1TIME.
91 *
92 * @returns IPRT status code.
93 * @param pCursor The cursor to use when reporting an error.
94 * @param pThis The time to parse.
95 * @param pszErrorTag The error tag.
96 */
97static int rtAsn1Time_ConvertUTCTime(PRTASN1CURSOR pCursor, PRTASN1TIME pThis, const char *pszErrorTag)
98{
99 /*
100 * While the current spec says the seconds field is not optional, this
101 * restriction was added later on. So, when parsing UTCTime we must deal
102 * with it being absent.
103 */
104 int rc;
105 bool fHaveSeconds = pThis->Asn1Core.cb == sizeof("YYMMDDHHMMSSZ") - 1;
106 if (fHaveSeconds || pThis->Asn1Core.cb == sizeof("YYMMDDHHMMZ") - 1)
107 {
108 const char *pachTime = pThis->Asn1Core.uData.pch;
109
110 /* Basic encoding validation. */
111 if ( RT_C_IS_DIGIT(pachTime[0]) /* Y */
112 && RT_C_IS_DIGIT(pachTime[1]) /* Y */
113 && RT_C_IS_DIGIT(pachTime[2]) /* M */
114 && RT_C_IS_DIGIT(pachTime[3]) /* M */
115 && RT_C_IS_DIGIT(pachTime[4]) /* D */
116 && RT_C_IS_DIGIT(pachTime[5]) /* D */
117 && RT_C_IS_DIGIT(pachTime[6]) /* H */
118 && RT_C_IS_DIGIT(pachTime[7]) /* H */
119 && RT_C_IS_DIGIT(pachTime[8]) /* M */
120 && RT_C_IS_DIGIT(pachTime[9]) /* M */
121 && ( !fHaveSeconds
122 || ( RT_C_IS_DIGIT(pachTime[10]) /* S */
123 && RT_C_IS_DIGIT(pachTime[11]) /* S */ ) )
124 && pachTime[fHaveSeconds ? 12 : 10] == 'Z'
125 )
126 {
127 /* Basic conversion. */
128 pThis->Time.i32Year = (pachTime[0] - '0') * 10 + (pachTime[1] - '0');
129 pThis->Time.i32Year += pThis->Time.i32Year < 50 ? 2000 : 1900;
130 pThis->Time.u8Month = (pachTime[2] - '0') * 10 + (pachTime[3] - '0');
131 pThis->Time.u8WeekDay = 0;
132 pThis->Time.u16YearDay = 0;
133 pThis->Time.u8MonthDay = (pachTime[4] - '0') * 10 + (pachTime[5] - '0');
134 pThis->Time.u8Hour = (pachTime[6] - '0') * 10 + (pachTime[7] - '0');
135 pThis->Time.u8Minute = (pachTime[8] - '0') * 10 + (pachTime[9] - '0');
136 if (fHaveSeconds)
137 pThis->Time.u8Second = (pachTime[10] - '0') * 10 + (pachTime[11] - '0');
138 else
139 pThis->Time.u8Second = 0;
140 pThis->Time.u32Nanosecond = 0;
141 pThis->Time.fFlags = RTTIME_FLAGS_TYPE_UTC;
142 pThis->Time.offUTC = 0;
143
144 /* Check the convered data and normalize the time structure. */
145 rc = rtAsn1Time_NormalizeTime(pCursor, pThis, "UTCTime", pszErrorTag);
146 if (RT_SUCCESS(rc))
147 return rc;
148 }
149 else
150 rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_UTC_TIME_ENCODING, "%s: Bad UTCTime encoding: '%.*s'",
151 pszErrorTag, pThis->Asn1Core.cb, pachTime);
152 }
153 else
154 rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_UTC_TIME_ENCODING, "%s: Bad UTCTime length: %#x",
155 pszErrorTag, pThis->Asn1Core.cb);
156 RT_ZERO(*pThis);
157 return rc;
158}
159
160
161/**
162 * Converts the fraction part of a generalized time into nanoseconds.
163 *
164 * @returns IPRT status code.
165 * @param pCursor The cursor to use when reporting an error.
166 * @param pchFraction Pointer to the start of the fraction (dot).
167 * @param cchFraction The length of the fraction.
168 * @param pThis The time object we're working on,
169 * Time.u32Nanoseconds will be update.
170 * @param pszErrorTag The error tag.
171 */
172static int rtAsn1Time_ConvertGeneralizedTimeFraction(PRTASN1CURSOR pCursor, const char *pchFraction, uint32_t cchFraction,
173 PRTASN1TIME pThis, const char *pszErrorTag)
174{
175 pThis->Time.u32Nanosecond = 0;
176
177 /*
178 * Check the dot.
179 */
180 if (*pchFraction != '.')
181 return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING,
182 "%s: Expected GeneralizedTime fraction dot, found: '%c' ('%.*s')",
183 pszErrorTag, *pchFraction, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
184 pchFraction++;
185 cchFraction--;
186 if (!cchFraction)
187 return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING,
188 "%s: No digit following GeneralizedTime fraction dot: '%.*s'",
189 pszErrorTag, pThis->Asn1Core.cb, pThis->Asn1Core);
190
191 /*
192 * Do the conversion.
193 */
194 char chLastDigit;
195 uint32_t uMult = 1000000000;
196 do
197 {
198 char chDigit = chLastDigit = *pchFraction;
199 if (!RT_C_IS_DIGIT(chDigit))
200 return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING,
201 "%s: Bad GeneralizedTime fraction digit: '%.*s'",
202 pszErrorTag, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
203 pThis->Time.u32Nanosecond += uMult * (uint32_t)(chDigit - '0');
204
205 /* Advance */
206 cchFraction--;
207 pchFraction++;
208 uMult /= 10;
209 } while (cchFraction > 0 && uMult > 0);
210
211 /*
212 * Lazy bird: For now, we don't permit higher resolution than we can
213 * internally represent. Deal with this if it ever becomes an issue.
214 */
215 if (cchFraction > 0)
216 return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING,
217 "%s: Bad GeneralizedTime fraction too long: '%.*s'",
218 pszErrorTag, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
219 if (chLastDigit == '0')
220 return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING,
221 "%s: Trailing zeros not allowed for GeneralizedTime: '%.*s'",
222 pszErrorTag, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch);
223 return VINF_SUCCESS;
224}
225
226
227/**
228 * Converts the GeneralizedTime string into an the RTTIME member of RTASN1TIME.
229 *
230 * @returns IPRT status code.
231 * @param pCursor The cursor to use when reporting an error.
232 * @param pThis The time to parse.
233 * @param pszErrorTag The error tag.
234 */
235static int rtAsn1Time_ConvertGeneralizedTime(PRTASN1CURSOR pCursor, PRTASN1TIME pThis, const char *pszErrorTag)
236{
237 int rc;
238 if (pThis->Asn1Core.cb >= sizeof("YYYYMMDDHHMMSSZ") - 1)
239 {
240 const char *pachTime = pThis->Asn1Core.uData.pch;
241
242 /* Basic encoding validation. */
243 if ( RT_C_IS_DIGIT(pachTime[0]) /* Y */
244 && RT_C_IS_DIGIT(pachTime[1]) /* Y */
245 && RT_C_IS_DIGIT(pachTime[2]) /* Y */
246 && RT_C_IS_DIGIT(pachTime[3]) /* Y */
247 && RT_C_IS_DIGIT(pachTime[4]) /* M */
248 && RT_C_IS_DIGIT(pachTime[5]) /* M */
249 && RT_C_IS_DIGIT(pachTime[6]) /* D */
250 && RT_C_IS_DIGIT(pachTime[7]) /* D */
251 && RT_C_IS_DIGIT(pachTime[8]) /* H */
252 && RT_C_IS_DIGIT(pachTime[9]) /* H */
253 && RT_C_IS_DIGIT(pachTime[10]) /* M */
254 && RT_C_IS_DIGIT(pachTime[11]) /* M */
255 && RT_C_IS_DIGIT(pachTime[12]) /* S */ /** @todo was this once optional? */
256 && RT_C_IS_DIGIT(pachTime[13]) /* S */
257 && pachTime[pThis->Asn1Core.cb - 1] == 'Z'
258 )
259 {
260 /* Basic conversion. */
261 pThis->Time.i32Year = 1000 * (pachTime[0] - '0')
262 + 100 * (pachTime[1] - '0')
263 + 10 * (pachTime[2] - '0')
264 + (pachTime[3] - '0');
265 pThis->Time.u8Month = (pachTime[4] - '0') * 10 + (pachTime[5] - '0');
266 pThis->Time.u8WeekDay = 0;
267 pThis->Time.u16YearDay = 0;
268 pThis->Time.u8MonthDay = (pachTime[6] - '0') * 10 + (pachTime[7] - '0');
269 pThis->Time.u8Hour = (pachTime[8] - '0') * 10 + (pachTime[9] - '0');
270 pThis->Time.u8Minute = (pachTime[10] - '0') * 10 + (pachTime[11] - '0');
271 pThis->Time.u8Second = (pachTime[12] - '0') * 10 + (pachTime[13] - '0');
272 pThis->Time.u32Nanosecond = 0;
273 pThis->Time.fFlags = RTTIME_FLAGS_TYPE_UTC;
274 pThis->Time.offUTC = 0;
275
276 /* Optional fraction part. */
277 rc = VINF_SUCCESS;
278 uint32_t cchLeft = pThis->Asn1Core.cb - 14 - 1;
279 if (cchLeft > 0)
280 rc = rtAsn1Time_ConvertGeneralizedTimeFraction(pCursor, pachTime + 14, cchLeft, pThis, pszErrorTag);
281
282 /* Check the convered data and normalize the time structure. */
283 if (RT_SUCCESS(rc))
284 {
285 rc = rtAsn1Time_NormalizeTime(pCursor, pThis, "GeneralizedTime", pszErrorTag);
286 if (RT_SUCCESS(rc))
287 return VINF_SUCCESS;
288 }
289 }
290 else
291 rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING,
292 "%s: Bad GeneralizedTime encoding: '%.*s'",
293 pszErrorTag, pThis->Asn1Core.cb, pachTime);
294 }
295 else
296 rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING,
297 "%s: Bad GeneralizedTime length: %#x",
298 pszErrorTag, pThis->Asn1Core.cb);
299 RT_ZERO(*pThis);
300 return rc;
301}
302
303
304RTDECL(int) RTAsn1Time_DecodeAsn1(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1TIME pThis, const char *pszErrorTag)
305{
306 Assert(!(fFlags & RTASN1CURSOR_GET_F_IMPLICIT));
307 int rc = RTAsn1CursorReadHdr(pCursor, &pThis->Asn1Core, pszErrorTag);
308 if (RT_SUCCESS(rc))
309 {
310 if (pThis->Asn1Core.fClass == (ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE) )
311 {
312 if (pThis->Asn1Core.uTag == ASN1_TAG_UTC_TIME)
313 {
314 RTAsn1CursorSkip(pCursor, pThis->Asn1Core.cb);
315 pThis->Asn1Core.pOps = &g_RTAsn1Time_Vtable;
316 pThis->Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
317 return rtAsn1Time_ConvertUTCTime(pCursor, pThis, pszErrorTag);
318 }
319
320 if (pThis->Asn1Core.uTag == ASN1_TAG_GENERALIZED_TIME)
321 {
322 RTAsn1CursorSkip(pCursor, pThis->Asn1Core.cb);
323 pThis->Asn1Core.pOps = &g_RTAsn1Time_Vtable;
324 pThis->Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
325 return rtAsn1Time_ConvertGeneralizedTime(pCursor, pThis, pszErrorTag);
326 }
327
328 rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_TAG_MISMATCH, "%s: Not UTCTime nor GeneralizedTime: uTag=%#x",
329 pszErrorTag, pThis->Asn1Core.uTag);
330 }
331 else
332 rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_CURSOR_TAG_FLAG_CLASS_MISMATCH,
333 "%s: Not UTCTime nor GeneralizedTime: fClass=%#x / uTag=%#x",
334 pszErrorTag, pThis->Asn1Core.fClass, pThis->Asn1Core.uTag);
335 }
336 RT_ZERO(*pThis);
337 return rc;
338}
339
340
341RTDECL(int) RTAsn1UtcTime_DecodeAsn1(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1TIME pThis, const char *pszErrorTag)
342{
343 int rc = RTAsn1CursorReadHdr(pCursor, &pThis->Asn1Core, pszErrorTag);
344 if (RT_SUCCESS(rc))
345 {
346 rc = RTAsn1CursorMatchTagClassFlags(pCursor, &pThis->Asn1Core, ASN1_TAG_UTC_TIME,
347 ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
348 fFlags, pszErrorTag, "UTC TIME");
349 if (RT_SUCCESS(rc))
350 {
351 RTAsn1CursorSkip(pCursor, pThis->Asn1Core.cb);
352 pThis->Asn1Core.pOps = &g_RTAsn1Time_Vtable;
353 pThis->Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
354 return rtAsn1Time_ConvertUTCTime(pCursor, pThis, pszErrorTag);
355 }
356 }
357 RT_ZERO(*pThis);
358 return rc;
359}
360
361
362RTDECL(int) RTAsn1GeneralizedTime_DecodeAsn1(PRTASN1CURSOR pCursor, uint32_t fFlags, PRTASN1TIME pThis, const char *pszErrorTag)
363{
364 int rc = RTAsn1CursorReadHdr(pCursor, &pThis->Asn1Core, pszErrorTag);
365 if (RT_SUCCESS(rc))
366 {
367 rc = RTAsn1CursorMatchTagClassFlags(pCursor, &pThis->Asn1Core, ASN1_TAG_GENERALIZED_TIME,
368 ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_PRIMITIVE,
369 fFlags, pszErrorTag, "GENERALIZED TIME");
370 if (RT_SUCCESS(rc))
371 {
372 RTAsn1CursorSkip(pCursor, pThis->Asn1Core.cb);
373 pThis->Asn1Core.pOps = &g_RTAsn1Time_Vtable;
374 pThis->Asn1Core.fFlags |= RTASN1CORE_F_PRIMITE_TAG_STRUCT;
375 return rtAsn1Time_ConvertGeneralizedTime(pCursor, pThis, pszErrorTag);
376 }
377 }
378 RT_ZERO(*pThis);
379 return rc;
380}
381
382
383/*
384 * Generate code for the associated collection types.
385 */
386#define RTASN1TMPL_TEMPLATE_FILE "../common/asn1/asn1-ut-time-template.h"
387#include <iprt/asn1-generator-internal-header.h>
388#include <iprt/asn1-generator-asn1-decoder.h>
389
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