2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 #include "utilities/SecCFRelease.h"
26 #include "utilities/SecCFWrappers.h"
27 #include "utilities/der_date.h"
28 #include "utilities/der_plist.h"
29 #include "utilities/der_plist_internal.h"
31 #include <corecrypto/ccder.h>
32 #include <CoreFoundation/CoreFoundation.h>
33 #include <CoreFoundation/CFCalendar.h>
37 #define IS_NULL_TIME(x) isnan(x)
39 /* Cumulative number of days in the year for months up to month i. */
40 static int mdays
[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
42 static bool validateDateComponents(int year
, int month
, int day
, int hour
, int minute
, int second
, int *is_leap_year
, CFErrorRef
* error
)
44 int leapyear
= year
% 4 == 0 && (year
% 100 != 0 || year
% 400 == 0) ? 1 : 0;
46 *is_leap_year
= leapyear
;
48 if (month
< 1 || month
> 12 || day
< 1 || day
> 31 || hour
>= 24 || minute
>= 60 || second
> 61
49 || (month
== 2 && day
> mdays
[month
] - mdays
[month
- 1] + leapyear
)
50 || (month
!= 2 && day
> mdays
[month
] - mdays
[month
- 1]))
52 SecError(kSecDERErrorUnknownEncoding
, error
, CFSTR("Invalid date: %i, %i, %i, %i, %i, %i, %i"), year
, month
, day
, hour
, minute
, second
, leapyear
);
59 static CFAbsoluteTime
SecGregorianDateGetAbsoluteTime(int year
, int month
, int day
, int hour
, int minute
, int second
, CFTimeInterval timeZoneOffset
, CFErrorRef
*error
) {
61 if (!validateDateComponents(year
, month
, day
, hour
, minute
, second
, &is_leap_year
, error
)) {
71 int leap_days
= dy
/ 4 - dy
/ 100 + dy
/ 400;
72 day
+= ((year
- 2001) * 365 + leap_days
) + mdays
[month
- 1] - 1;
76 CFAbsoluteTime absTime
= (CFAbsoluteTime
)((day
* 24 + hour
) * 60 + minute
) * 60 + second
;
77 return absTime
- timeZoneOffset
;
80 static bool SecAbsoluteTimeGetGregorianDate(CFTimeInterval at
, int *year
, int *month
, int *day
, int *hour
, int *minute
, int *second
, CFErrorRef
*error
) {
81 // TODO: Remove CFCalendarDecomposeAbsoluteTime dependancy because
82 // CFTimeZoneCreateWithTimeIntervalFromGMT is expensive and requires
83 // filesystem access to timezone files when we are only doing zulu time anyway
85 SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar
) {
86 result
= CFCalendarDecomposeAbsoluteTime(zuluCalendar
, at
, "yMdHms", year
, month
, day
, hour
, minute
, second
);
89 SecCFDERCreateError(kSecDERErrorUnknownEncoding
, CFSTR("Failed to encode date."), 0, error
);
95 static int der_get_char(const uint8_t **der_p
, const uint8_t *der_end
,
97 const uint8_t *der
= *der_p
;
99 /* Don't create a new error in this case. */
103 if (der
>= der_end
) {
104 SecCFDERCreateError(kSecDERErrorUnknownEncoding
,
105 CFSTR("Unexpected end of datetime"), 0, error
);
116 static int der_decode_decimal(const uint8_t **der_p
, const uint8_t *der_end
,
118 char ch
= der_get_char(der_p
, der_end
, error
);
119 if (ch
< '0' || ch
> '9') {
120 SecCFDERCreateError(kSecDERErrorUnknownEncoding
,
121 CFSTR("Not a decimal digit"), 0, error
);
128 static int der_decode_decimal_pair(const uint8_t **der_p
, const uint8_t *der_end
,
130 return (10 * der_decode_decimal(der_p
, der_end
, error
))
131 + der_decode_decimal(der_p
, der_end
, error
);
134 static int der_peek_byte(const uint8_t *der
, const uint8_t *der_end
) {
135 if (!der
|| der
>= der_end
)
141 static const uint8_t *der_decode_decimal_fraction(double *fraction
, CFErrorRef
*error
,
142 const uint8_t* der
, const uint8_t *der_end
) {
143 int ch
= der_peek_byte(der
, der_end
);
146 } else if (ch
== '.') {
147 uint64_t divisor
= 1;
150 while (++der
< der_end
) {
153 if (ch
< '0' || ch
> '9') {
156 if (divisor
< UINT64_MAX
/ 10) {
162 if (der
>= der_end
) {
163 SecCFDERCreateError(kSecDERErrorOverflow
,
164 CFSTR("overflow"), 0, error
);
166 } else if (last
== '0') {
167 SecCFDERCreateError(kSecDERErrorUnknownEncoding
,
168 CFSTR("fraction ends in 0"), 0, error
);
170 } else if (last
== '.') {
171 SecCFDERCreateError(kSecDERErrorUnknownEncoding
,
172 CFSTR("fraction without digits"), 0, error
);
175 *fraction
= (double)value
/ divisor
;
184 static CFTimeInterval
der_decode_timezone_offset(const uint8_t **der_p
,
185 const uint8_t *der_end
,
187 CFTimeInterval timeZoneOffset
;
188 int ch
= der_get_char(der_p
, der_end
, error
);
191 timeZoneOffset
= 0.0;
200 SecCFDERCreateError(kSecDERErrorUnknownEncoding
,
201 CFSTR("Invalid datetime character"), 0, error
);
205 timeZoneOffset
= multiplier
*
206 (der_decode_decimal_pair(der_p
, der_end
, error
)
207 * 60 + der_decode_decimal_pair(der_p
, der_end
, error
));
209 return timeZoneOffset
;
212 static const uint8_t* der_decode_commontime_body(CFAbsoluteTime
*at
, CFErrorRef
*error
, int year
,
213 const uint8_t* der
, const uint8_t *der_end
)
215 int month
= der_decode_decimal_pair(&der
, der_end
, error
);
216 int day
= der_decode_decimal_pair(&der
, der_end
, error
);
217 int hour
= der_decode_decimal_pair(&der
, der_end
, error
);
218 int minute
= der_decode_decimal_pair(&der
, der_end
, error
);
219 int second
= der_decode_decimal_pair(&der
, der_end
, error
);
221 der
= der_decode_decimal_fraction(&fraction
, error
, der
, der_end
);
223 CFTimeInterval timeZoneOffset
= der_decode_timezone_offset(&der
, der_end
, error
);
226 if (der
!= der_end
) {
227 SecCFDERCreateError(kSecDERErrorUnknownEncoding
,
228 CFSTR("trailing garbage at end of datetime"), 0, error
);
232 *at
= SecGregorianDateGetAbsoluteTime(year
, month
, day
, hour
, minute
, second
, timeZoneOffset
, error
);
233 if (IS_NULL_TIME(*at
))
242 const uint8_t* der_decode_generalizedtime_body(CFAbsoluteTime
*at
, CFErrorRef
*error
,
243 const uint8_t* der
, const uint8_t *der_end
)
245 int year
= 100 * der_decode_decimal_pair(&der
, der_end
, error
) + der_decode_decimal_pair(&der
, der_end
, error
);
246 return der_decode_commontime_body(at
, error
, year
, der
, der_end
);
249 const uint8_t* der_decode_universaltime_body(CFAbsoluteTime
*at
, CFErrorRef
*error
,
250 const uint8_t* der
, const uint8_t *der_end
)
252 SInt32 year
= der_decode_decimal_pair(&der
, der_end
, error
);
254 /* 0 <= year < 50 : assume century 21 */
256 } else if (year
< 70) {
257 /* 50 <= year < 70 : illegal per PKIX */
258 SecCFDERCreateError(kSecDERErrorUnknownEncoding
,
259 CFSTR("Invalid universal time year between 50 and 70"), 0, error
);
262 /* 70 < year <= 99 : assume century 20 */
266 return der_decode_commontime_body(at
, error
, year
, der
, der_end
);
269 const uint8_t* der_decode_date(CFAllocatorRef allocator
,
270 CFDateRef
* date
, CFErrorRef
*error
,
271 const uint8_t* der
, const uint8_t *der_end
)
274 SecCFDERCreateError(kSecDERErrorNullInput
, CFSTR("null input"), NULL
, error
);
278 der
= ccder_decode_constructed_tl(CCDER_GENERALIZED_TIME
, &der_end
, der
, der_end
);
279 CFAbsoluteTime at
= 0;
280 der
= der_decode_generalizedtime_body(&at
, error
, der
, der_end
);
282 *date
= CFDateCreate(allocator
, at
);
284 SecCFDERCreateError(kSecDERErrorAllocationFailure
, CFSTR("Failed to create date"), NULL
, error
);
291 extern char *__dtoa(double _d
, int mode
, int ndigits
, int *decpt
, int *sign
, char **rve
);
292 extern void __freedtoa(char *);
294 static size_t ccder_sizeof_nanoseconds(CFAbsoluteTime at
) {
298 char *str
= __dtoa(at
, 0, 0, &dotoff
, &sign
, &end
);
299 ptrdiff_t len
= end
- str
;
301 return len
< dotoff
? 0 : len
- dotoff
;
302 //return len < dotoff ? 0 : len - dotoff > 9 ? 9 : len - dotoff;
305 size_t der_sizeof_generalizedtime_body(CFAbsoluteTime at
, CFErrorRef
*error
)
307 size_t subsec_digits
= ccder_sizeof_nanoseconds(at
);
309 /* Generalized zulu time YYYYMMDDhhmmss[.ssss]Z */
310 return subsec_digits
? 16 + subsec_digits
: 15;
313 size_t der_sizeof_generalizedtime(CFAbsoluteTime at
, CFErrorRef
*error
)
315 return ccder_sizeof(CCDER_GENERALIZED_TIME
,
316 der_sizeof_generalizedtime_body(at
, error
));
319 size_t der_sizeof_date(CFDateRef date
, CFErrorRef
*error
)
321 return der_sizeof_generalizedtime(CFDateGetAbsoluteTime(date
), error
);
325 static uint8_t *ccder_encode_byte(uint8_t byte
,
326 const uint8_t *der
, uint8_t *der_end
) {
327 if (der
+ 1 > der_end
) {
334 static uint8_t *ccder_encode_decimal_pair(int v
, const uint8_t *der
,
336 if (der_end
== NULL
|| der
+ 2 > der_end
) {
340 *--der_end
= '0' + v
% 10;
341 *--der_end
= '0' + v
/ 10;
345 static uint8_t *ccder_encode_decimal_quad(int v
, const uint8_t *der
,
347 return ccder_encode_decimal_pair(v
/ 100, der
,
348 ccder_encode_decimal_pair(v
% 100, der
, der_end
));
351 static uint8_t *ccder_encode_nanoseconds(CFAbsoluteTime at
, const uint8_t *der
,
356 char *str
= __dtoa(at
, 0, 0, &dotoff
, &sign
, &end
);
357 char *begin
= str
+ (dotoff
< 0 ? 0 : dotoff
);
358 // Compute 1.0000000 - fraction in ascii space
359 if (at
< 0.0 && begin
< end
) {
361 // Borrow for last digit
362 *p
= ('9' + 1) - (*p
- '0');
363 while (p
-- > begin
) {
364 // Every other digit is a 9 since we borrowed from the last one
365 *p
= '9' - (*p
- '0');
369 ptrdiff_t len
= end
- str
;
372 assert(-1.0 < at
&& at
< 1.0);
373 der_end
= ccder_encode_body(len
, (const uint8_t *)str
, der
, der_end
);
374 der_end
= ccder_encode_body_nocopy(-dotoff
, der
, der_end
);
376 memset(der_end
, at
< 0.0 ? '9' : '0', -dotoff
);
378 der_end
= ccder_encode_body(len
- dotoff
, (const uint8_t *)(str
+ dotoff
), der
, der_end
);
380 der_end
= ccder_encode_byte('.', der
, der_end
);
387 uint8_t* der_encode_generalizedtime_body(CFAbsoluteTime at
, CFErrorRef
*error
,
388 const uint8_t *der
, uint8_t *der_end
)
390 return der_encode_generalizedtime_body_repair(at
, error
, false, der
, der_end
);
393 /* Encode generalized zulu time YYYYMMDDhhmmss[.ssss]Z */
394 uint8_t* der_encode_generalizedtime_body_repair(CFAbsoluteTime at
, CFErrorRef
*error
, bool repair
,
395 const uint8_t *der
, uint8_t *der_end
)
397 int year
= 0, month
= 0, day
= 0, hour
= 0, minute
= 0, second
= 0;
398 if (!SecAbsoluteTimeGetGregorianDate(at
, &year
, &month
, &day
, &hour
, &minute
, &second
, error
)) {
399 secerror("der: unable to encode date: %@", error
? *error
: NULL
);
403 // Without repair flag, do not let validation change result so we do not change existing behavior
404 CFErrorRef localError
= NULL
;
405 if (!validateDateComponents(year
, month
, day
, hour
, minute
, second
, 0, &localError
)) {
406 CFStringRef desc
= CFErrorCopyDescription(localError
);
407 __security_simulatecrash(desc
, __sec_exception_code_CorruptItem
);
409 secerror("der: invalid date: %@; %s", localError
, repair
? "setting default value" : "continuing");
410 CFReleaseNull(localError
);
415 // I don't think this is required but I don't want any funny business with exactly midnight in any timezone
420 uint8_t * result
= ccder_encode_decimal_quad(year
, der
,
421 ccder_encode_decimal_pair(month
, der
,
422 ccder_encode_decimal_pair(day
, der
,
423 ccder_encode_decimal_pair(hour
, der
,
424 ccder_encode_decimal_pair(minute
, der
,
425 ccder_encode_decimal_pair(second
, der
,
426 ccder_encode_nanoseconds(at
, der
, // Uses original "at" even after repair because DER length calculations depend on its exact value
427 ccder_encode_byte('Z', der
, der_end
))))))));
429 return SecCCDEREncodeHandleResult(result
, error
);
432 uint8_t* der_encode_generalizedtime(CFAbsoluteTime at
, CFErrorRef
*error
,
433 const uint8_t *der
, uint8_t *der_end
)
435 return SecCCDEREncodeHandleResult(ccder_encode_constructed_tl(CCDER_GENERALIZED_TIME
, der_end
, der
,
436 der_encode_generalizedtime_body(at
, error
, der
, der_end
)),
441 uint8_t* der_encode_date(CFDateRef date
, CFErrorRef
*error
,
442 const uint8_t *der
, uint8_t *der_end
)
444 return der_encode_generalizedtime(CFDateGetAbsoluteTime(date
), error
,
448 uint8_t* der_encode_date_repair(CFDateRef date
, CFErrorRef
*error
,
449 bool repair
, const uint8_t *der
, uint8_t *der_end
)
451 return SecCCDEREncodeHandleResult(ccder_encode_constructed_tl(CCDER_GENERALIZED_TIME
, der_end
, der
,
452 der_encode_generalizedtime_body_repair(CFDateGetAbsoluteTime(date
), error
, repair
, der
, der_end
)),