VirtualBox

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

最後變更 在這個檔案從75176是 75176,由 vboxsync 提交於 6 年 前

IPRT: Workaround for leap seconds and seconds rounding error on OS X in their DER_CFDateToUTCTime() function. bugref:9232

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette