VirtualBox

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

Last change on this file since 95596 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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