VirtualBox

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

© 2023 Oracle
ContactPrivacy policyTerms of Use