VirtualBox

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

Last change on this file since 73172 was 69111, checked in by vboxsync, 7 years ago

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