/* * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ #include #include #include #include #include "ext_dat.h" static const char *WEEKDAY_NAMES[7] = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" }; static const char *WEEK_NAMES[5] = { "first", "second", "third", "fourth", "final" }; static const char *MONTH_NAMES[12] = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEPT", "OCT", "NOV", "DEC" }; ASN1_SEQUENCE(OSSL_TIME_SPEC_ABSOLUTE) = { ASN1_EXP_OPT(OSSL_TIME_SPEC_ABSOLUTE, startTime, ASN1_GENERALIZEDTIME, 0), ASN1_EXP_OPT(OSSL_TIME_SPEC_ABSOLUTE, endTime, ASN1_GENERALIZEDTIME, 1), } ASN1_SEQUENCE_END(OSSL_TIME_SPEC_ABSOLUTE) ASN1_SEQUENCE(OSSL_DAY_TIME) = { ASN1_EXP_OPT(OSSL_DAY_TIME, hour, ASN1_INTEGER, 0), ASN1_EXP_OPT(OSSL_DAY_TIME, minute, ASN1_INTEGER, 1), ASN1_EXP_OPT(OSSL_DAY_TIME, second, ASN1_INTEGER, 2), } ASN1_SEQUENCE_END(OSSL_DAY_TIME) ASN1_SEQUENCE(OSSL_DAY_TIME_BAND) = { ASN1_EXP_OPT(OSSL_DAY_TIME_BAND, startDayTime, OSSL_DAY_TIME, 0), ASN1_EXP_OPT(OSSL_DAY_TIME_BAND, endDayTime, OSSL_DAY_TIME, 1), } ASN1_SEQUENCE_END(OSSL_DAY_TIME_BAND) ASN1_CHOICE(OSSL_NAMED_DAY) = { ASN1_SET_OF(OSSL_NAMED_DAY, choice.intNamedDays, ASN1_ENUMERATED), ASN1_SIMPLE(OSSL_NAMED_DAY, choice.bitNamedDays, ASN1_BIT_STRING), } ASN1_CHOICE_END(OSSL_NAMED_DAY) ASN1_CHOICE(OSSL_TIME_SPEC_X_DAY_OF) = { ASN1_EXP(OSSL_TIME_SPEC_X_DAY_OF, choice.first, OSSL_NAMED_DAY, 1), ASN1_EXP(OSSL_TIME_SPEC_X_DAY_OF, choice.second, OSSL_NAMED_DAY, 2), ASN1_EXP(OSSL_TIME_SPEC_X_DAY_OF, choice.third, OSSL_NAMED_DAY, 3), ASN1_EXP(OSSL_TIME_SPEC_X_DAY_OF, choice.fourth, OSSL_NAMED_DAY, 4), ASN1_EXP(OSSL_TIME_SPEC_X_DAY_OF, choice.fifth, OSSL_NAMED_DAY, 5), } ASN1_CHOICE_END(OSSL_TIME_SPEC_X_DAY_OF) ASN1_CHOICE(OSSL_TIME_SPEC_DAY) = { ASN1_SET_OF(OSSL_TIME_SPEC_DAY, choice.intDay, ASN1_INTEGER), ASN1_SIMPLE(OSSL_TIME_SPEC_DAY, choice.bitDay, ASN1_BIT_STRING), ASN1_SIMPLE(OSSL_TIME_SPEC_DAY, choice.dayOf, OSSL_TIME_SPEC_X_DAY_OF), } ASN1_CHOICE_END(OSSL_TIME_SPEC_DAY) ASN1_CHOICE(OSSL_TIME_SPEC_WEEKS) = { ASN1_SIMPLE(OSSL_TIME_SPEC_WEEKS, choice.allWeeks, ASN1_NULL), ASN1_SET_OF(OSSL_TIME_SPEC_WEEKS, choice.intWeek, ASN1_INTEGER), ASN1_SIMPLE(OSSL_TIME_SPEC_WEEKS, choice.bitWeek, ASN1_BIT_STRING), } ASN1_CHOICE_END(OSSL_TIME_SPEC_WEEKS) ASN1_CHOICE(OSSL_TIME_SPEC_MONTH) = { ASN1_SIMPLE(OSSL_TIME_SPEC_MONTH, choice.allMonths, ASN1_NULL), ASN1_SET_OF(OSSL_TIME_SPEC_MONTH, choice.intMonth, ASN1_INTEGER), ASN1_SIMPLE(OSSL_TIME_SPEC_MONTH, choice.bitMonth, ASN1_BIT_STRING), } ASN1_CHOICE_END(OSSL_TIME_SPEC_MONTH) ASN1_SEQUENCE(OSSL_TIME_PERIOD) = { ASN1_EXP_SET_OF_OPT(OSSL_TIME_PERIOD, timesOfDay, OSSL_DAY_TIME_BAND, 0), ASN1_EXP_OPT(OSSL_TIME_PERIOD, days, OSSL_TIME_SPEC_DAY, 1), ASN1_EXP_OPT(OSSL_TIME_PERIOD, weeks, OSSL_TIME_SPEC_WEEKS, 2), ASN1_EXP_OPT(OSSL_TIME_PERIOD, months, OSSL_TIME_SPEC_MONTH, 3), ASN1_EXP_SET_OF_OPT(OSSL_TIME_PERIOD, years, ASN1_INTEGER, 4), } ASN1_SEQUENCE_END(OSSL_TIME_PERIOD) ASN1_CHOICE(OSSL_TIME_SPEC_TIME) = { ASN1_SIMPLE(OSSL_TIME_SPEC_TIME, choice.absolute, OSSL_TIME_SPEC_ABSOLUTE), ASN1_SET_OF(OSSL_TIME_SPEC_TIME, choice.periodic, OSSL_TIME_PERIOD) } ASN1_CHOICE_END(OSSL_TIME_SPEC_TIME) ASN1_SEQUENCE(OSSL_TIME_SPEC) = { ASN1_SIMPLE(OSSL_TIME_SPEC, time, OSSL_TIME_SPEC_TIME), ASN1_OPT(OSSL_TIME_SPEC, notThisTime, ASN1_FBOOLEAN), ASN1_OPT(OSSL_TIME_SPEC, timeZone, ASN1_INTEGER), } ASN1_SEQUENCE_END(OSSL_TIME_SPEC) IMPLEMENT_ASN1_FUNCTIONS(OSSL_DAY_TIME) IMPLEMENT_ASN1_FUNCTIONS(OSSL_DAY_TIME_BAND) IMPLEMENT_ASN1_FUNCTIONS(OSSL_TIME_SPEC_DAY) IMPLEMENT_ASN1_FUNCTIONS(OSSL_TIME_SPEC_WEEKS) IMPLEMENT_ASN1_FUNCTIONS(OSSL_TIME_SPEC_MONTH) IMPLEMENT_ASN1_FUNCTIONS(OSSL_NAMED_DAY) IMPLEMENT_ASN1_FUNCTIONS(OSSL_TIME_SPEC_X_DAY_OF) IMPLEMENT_ASN1_FUNCTIONS(OSSL_TIME_SPEC_ABSOLUTE) IMPLEMENT_ASN1_FUNCTIONS(OSSL_TIME_SPEC_TIME) IMPLEMENT_ASN1_FUNCTIONS(OSSL_TIME_SPEC) IMPLEMENT_ASN1_FUNCTIONS(OSSL_TIME_PERIOD) static int i2r_OSSL_TIME_SPEC_ABSOLUTE(X509V3_EXT_METHOD *method, OSSL_TIME_SPEC_ABSOLUTE *time, BIO *out, int indent) { if (time->startTime != NULL && time->endTime != NULL) { if (!BIO_puts(out, "Any time between ")) return 0; if (!ossl_asn1_time_print_ex(out, time->startTime, 0)) return 0; if (!BIO_puts(out, " and ")) return 0; if (!ossl_asn1_time_print_ex(out, time->endTime, 0)) return 0; } else if (time->startTime != NULL) { if (!BIO_puts(out, "Any time after ")) return 0; if (!ossl_asn1_time_print_ex(out, time->startTime, 0)) return 0; if (BIO_printf(out, "%.*s", time->startTime->length, time->startTime->data) <= 0) return 0; } else if (time->endTime != NULL) { if (!BIO_puts(out, "Any time until ")) return 0; if (!ossl_asn1_time_print_ex(out, time->endTime, 0)) return 0; } else { /* Invalid: there must be SOME time specified. */ return BIO_puts(out, "INVALID (EMPTY)"); } return 1; } static int i2r_OSSL_DAY_TIME(X509V3_EXT_METHOD *method, OSSL_DAY_TIME *dt, BIO *out, int indent) { int64_t h = 0; int64_t m = 0; int64_t s = 0; if (!dt->hour || !ASN1_INTEGER_get_int64(&h, dt->hour)) return 0; if (dt->minute && !ASN1_INTEGER_get_int64(&m, dt->minute)) return 0; if (dt->minute && !ASN1_INTEGER_get_int64(&s, dt->second)) return 0; return BIO_printf(out, "%02lld:%02lld:%02lld", (long long int)h, (long long int)m, (long long int)s) > 0; } static int i2r_OSSL_DAY_TIME_BAND(X509V3_EXT_METHOD *method, OSSL_DAY_TIME_BAND *band, BIO *out, int indent) { if (band->startDayTime) { if (!i2r_OSSL_DAY_TIME(method, band->startDayTime, out, indent)) return 0; } else if (!BIO_puts(out, "00:00:00")) { return 0; } if (!BIO_puts(out, " - ")) return 0; if (band->endDayTime) { if (!i2r_OSSL_DAY_TIME(method, band->endDayTime, out, indent)) return 0; } else if (!BIO_puts(out, "23:59:59")) { return 0; } return 1; } static int print_int_month(BIO *out, int64_t month) { switch (month) { case (OSSL_TIME_SPEC_INT_MONTH_JAN): return BIO_puts(out, "JAN"); case (OSSL_TIME_SPEC_INT_MONTH_FEB): return BIO_puts(out, "FEB"); case (OSSL_TIME_SPEC_INT_MONTH_MAR): return BIO_puts(out, "MAR"); case (OSSL_TIME_SPEC_INT_MONTH_APR): return BIO_puts(out, "APR"); case (OSSL_TIME_SPEC_INT_MONTH_MAY): return BIO_puts(out, "MAY"); case (OSSL_TIME_SPEC_INT_MONTH_JUN): return BIO_puts(out, "JUN"); case (OSSL_TIME_SPEC_INT_MONTH_JUL): return BIO_puts(out, "JUL"); case (OSSL_TIME_SPEC_INT_MONTH_AUG): return BIO_puts(out, "AUG"); case (OSSL_TIME_SPEC_INT_MONTH_SEP): return BIO_puts(out, "SEP"); case (OSSL_TIME_SPEC_INT_MONTH_OCT): return BIO_puts(out, "OCT"); case (OSSL_TIME_SPEC_INT_MONTH_NOV): return BIO_puts(out, "NOV"); case (OSSL_TIME_SPEC_INT_MONTH_DEC): return BIO_puts(out, "DEC"); default: return 0; } return 0; } static int print_bit_month(BIO *out, ASN1_BIT_STRING *bs) { int i = OSSL_TIME_SPEC_BIT_MONTH_JAN; int j = 0; for (; i <= OSSL_TIME_SPEC_BIT_MONTH_DEC; i++) { if (ASN1_BIT_STRING_get_bit(bs, i)) { if (j > 0 && !BIO_puts(out, ", ")) return 0; j++; if (!BIO_puts(out, MONTH_NAMES[i])) return 0; } } return 1; } /* * It might seem like you could just print the bits of the string numerically, * but the fifth bit has the special meaning of "the final week" imputed to it * by the text of ITU-T Recommendation X.520. */ static int print_bit_week(BIO *out, ASN1_BIT_STRING *bs) { int i = OSSL_TIME_SPEC_BIT_WEEKS_1; int j = 0; for (; i <= OSSL_TIME_SPEC_BIT_WEEKS_5; i++) { if (ASN1_BIT_STRING_get_bit(bs, i)) { if (j > 0 && !BIO_puts(out, ", ")) return 0; j++; if (!BIO_puts(out, WEEK_NAMES[i])) return 0; } } return 1; } static int print_day_of_week(BIO *out, ASN1_BIT_STRING *bs) { int i = OSSL_TIME_SPEC_DAY_BIT_SUN; int j = 0; for (; i <= OSSL_TIME_SPEC_DAY_BIT_SAT; i++) { if (ASN1_BIT_STRING_get_bit(bs, i)) { if (j > 0 && !BIO_puts(out, ", ")) return 0; j++; if (!BIO_puts(out, WEEKDAY_NAMES[i])) return 0; } } return 1; } static int print_int_day_of_week(BIO *out, int64_t dow) { switch (dow) { case (OSSL_TIME_SPEC_DAY_INT_SUN): return BIO_puts(out, "SUN"); case (OSSL_TIME_SPEC_DAY_INT_MON): return BIO_puts(out, "MON"); case (OSSL_TIME_SPEC_DAY_INT_TUE): return BIO_puts(out, "TUE"); case (OSSL_TIME_SPEC_DAY_INT_WED): return BIO_puts(out, "WED"); case (OSSL_TIME_SPEC_DAY_INT_THU): return BIO_puts(out, "THU"); case (OSSL_TIME_SPEC_DAY_INT_FRI): return BIO_puts(out, "FRI"); case (OSSL_TIME_SPEC_DAY_INT_SAT): return BIO_puts(out, "SAT"); default: return 0; } return 0; } static int print_int_named_day(BIO *out, int64_t nd) { switch (nd) { case (OSSL_NAMED_DAY_INT_SUN): return BIO_puts(out, "SUN"); case (OSSL_NAMED_DAY_INT_MON): return BIO_puts(out, "MON"); case (OSSL_NAMED_DAY_INT_TUE): return BIO_puts(out, "TUE"); case (OSSL_NAMED_DAY_INT_WED): return BIO_puts(out, "WED"); case (OSSL_NAMED_DAY_INT_THU): return BIO_puts(out, "THU"); case (OSSL_NAMED_DAY_INT_FRI): return BIO_puts(out, "FRI"); case (OSSL_NAMED_DAY_INT_SAT): return BIO_puts(out, "SAT"); default: return 0; } return 0; } static int print_bit_named_day(BIO *out, ASN1_BIT_STRING *bs) { return print_day_of_week(out, bs); } static int i2r_OSSL_PERIOD(X509V3_EXT_METHOD *method, OSSL_TIME_PERIOD *p, BIO *out, int indent) { int i; OSSL_DAY_TIME_BAND *band; ASN1_INTEGER *big_val; int64_t small_val; OSSL_NAMED_DAY *nd; if (BIO_printf(out, "%*sPeriod:\n", indent, "") <= 0) return 0; if (p->timesOfDay) { if (BIO_printf(out, "%*sDaytime bands:\n", indent + 4, "") <= 0) return 0; for (i = 0; i < sk_OSSL_DAY_TIME_BAND_num(p->timesOfDay); i++) { band = sk_OSSL_DAY_TIME_BAND_value(p->timesOfDay, i); if (BIO_printf(out, "%*s", indent + 8, "") <= 0) return 0; if (!i2r_OSSL_DAY_TIME_BAND(method, band, out, indent + 8)) return 0; if (!BIO_puts(out, "\n")) return 0; } } if (p->days) { if (p->days->type == OSSL_TIME_SPEC_DAY_TYPE_INT) { if (p->weeks != NULL) { if (BIO_printf(out, "%*sDays of the week: ", indent + 4, "") <= 0) return 0; } else if (p->months != NULL) { if (BIO_printf(out, "%*sDays of the month: ", indent + 4, "") <= 0) return 0; } else if (p->years != NULL) { if (BIO_printf(out, "%*sDays of the year: ", indent + 4, "") <= 0) return 0; } } else { if (BIO_printf(out, "%*sDays: ", indent + 4, "") <= 0) return 0; } switch (p->days->type) { case (OSSL_TIME_SPEC_DAY_TYPE_INT): for (i = 0; i < sk_ASN1_INTEGER_num(p->days->choice.intDay); i++) { big_val = sk_ASN1_INTEGER_value(p->days->choice.intDay, i); if (!ASN1_INTEGER_get_int64(&small_val, big_val)) return 0; if (i > 0 && !BIO_puts(out, ", ")) return 0; /* If weeks is defined, then print day of week by name. */ if (p->weeks != NULL) { if (!print_int_day_of_week(out, small_val)) return 0; } else if (BIO_printf(out, "%lld", (long long int)small_val) <= 0) { return 0; } } break; case (OSSL_TIME_SPEC_DAY_TYPE_BIT): if (!print_day_of_week(out, p->days->choice.bitDay)) return 0; break; case (OSSL_TIME_SPEC_DAY_TYPE_DAY_OF): switch (p->days->choice.dayOf->type) { case (OSSL_TIME_SPEC_X_DAY_OF_FIRST): if (!BIO_puts(out, "FIRST ")) return 0; nd = p->days->choice.dayOf->choice.first; break; case (OSSL_TIME_SPEC_X_DAY_OF_SECOND): if (!BIO_puts(out, "SECOND ")) return 0; nd = p->days->choice.dayOf->choice.second; break; case (OSSL_TIME_SPEC_X_DAY_OF_THIRD): if (!BIO_puts(out, "THIRD ")) return 0; nd = p->days->choice.dayOf->choice.third; break; case (OSSL_TIME_SPEC_X_DAY_OF_FOURTH): if (!BIO_puts(out, "FOURTH ")) return 0; nd = p->days->choice.dayOf->choice.fourth; break; case (OSSL_TIME_SPEC_X_DAY_OF_FIFTH): if (!BIO_puts(out, "FIFTH ")) return 0; nd = p->days->choice.dayOf->choice.fifth; break; default: return 0; } switch (nd->type) { case (OSSL_NAMED_DAY_TYPE_INT): if (!ASN1_INTEGER_get_int64(&small_val, nd->choice.intNamedDays)) return 0; if (!print_int_named_day(out, small_val)) return 0; break; case (OSSL_NAMED_DAY_TYPE_BIT): if (!print_bit_named_day(out, nd->choice.bitNamedDays)) return 0; break; default: return 0; } break; default: return 0; } if (!BIO_puts(out, "\n")) return 0; } if (p->weeks) { if (p->weeks->type == OSSL_TIME_SPEC_WEEKS_TYPE_INT) { if (p->months != NULL) { if (BIO_printf(out, "%*sWeeks of the month: ", indent + 4, "") <= 0) return 0; } else if (p->years != NULL) { if (BIO_printf(out, "%*sWeeks of the year: ", indent + 4, "") <= 0) return 0; } } else { if (BIO_printf(out, "%*sWeeks: ", indent + 4, "") <= 0) return 0; } switch (p->weeks->type) { case (OSSL_TIME_SPEC_WEEKS_TYPE_ALL): if (!BIO_puts(out, "ALL")) return 0; break; case (OSSL_TIME_SPEC_WEEKS_TYPE_INT): for (i = 0; i < sk_ASN1_INTEGER_num(p->weeks->choice.intWeek); i++) { big_val = sk_ASN1_INTEGER_value(p->weeks->choice.intWeek, i); if (!ASN1_INTEGER_get_int64(&small_val, big_val)) return 0; if (i > 0 && !BIO_puts(out, ", ")) return 0; if (!BIO_printf(out, "%lld", (long long int)small_val)) return 0; } break; case (OSSL_TIME_SPEC_WEEKS_TYPE_BIT): if (!print_bit_week(out, p->weeks->choice.bitWeek)) return 0; break; default: return 0; } if (!BIO_puts(out, "\n")) return 0; } if (p->months) { if (BIO_printf(out, "%*sMonths: ", indent + 4, "") <= 0) return 0; switch (p->months->type) { case (OSSL_TIME_SPEC_MONTH_TYPE_ALL): if (!BIO_puts(out, "ALL")) return 0; break; case (OSSL_TIME_SPEC_MONTH_TYPE_INT): for (i = 0; i < sk_ASN1_INTEGER_num(p->months->choice.intMonth); i++) { big_val = sk_ASN1_INTEGER_value(p->months->choice.intMonth, i); if (!ASN1_INTEGER_get_int64(&small_val, big_val)) return 0; if (i > 0 && !BIO_puts(out, ", ")) return 0; if (!print_int_month(out, small_val)) return 0; } break; case (OSSL_TIME_SPEC_MONTH_TYPE_BIT): if (!print_bit_month(out, p->months->choice.bitMonth)) return 0; break; default: return 0; } if (!BIO_puts(out, "\n")) return 0; } if (p->years) { if (BIO_printf(out, "%*sYears: ", indent + 4, "") <= 0) return 0; for (i = 0; i < sk_ASN1_INTEGER_num(p->years); i++) { big_val = sk_ASN1_INTEGER_value(p->years, i); if (!ASN1_INTEGER_get_int64(&small_val, big_val)) return 0; if (i > 0 && !BIO_puts(out, ", ")) return 0; if (BIO_printf(out, "%04lld", (long long int)small_val) <= 0) return 0; } } return 1; } static int i2r_OSSL_TIME_SPEC_TIME(X509V3_EXT_METHOD *method, OSSL_TIME_SPEC_TIME *time, BIO *out, int indent) { OSSL_TIME_PERIOD *tp; int i; switch (time->type) { case (OSSL_TIME_SPEC_TIME_TYPE_ABSOLUTE): if (BIO_printf(out, "%*sAbsolute: ", indent, "") <= 0) return 0; if (i2r_OSSL_TIME_SPEC_ABSOLUTE(method, time->choice.absolute, out, indent + 4) <= 0) return 0; return BIO_puts(out, "\n"); case (OSSL_TIME_SPEC_TIME_TYPE_PERIODIC): if (BIO_printf(out, "%*sPeriodic:\n", indent, "") <= 0) return 0; for (i = 0; i < sk_OSSL_TIME_PERIOD_num(time->choice.periodic); i++) { if (i > 0 && !BIO_puts(out, "\n")) return 0; tp = sk_OSSL_TIME_PERIOD_value(time->choice.periodic, i); if (!i2r_OSSL_PERIOD(method, tp, out, indent + 4)) return 0; } return BIO_puts(out, "\n"); default: return 0; } return 0; } static int i2r_OSSL_TIME_SPEC(X509V3_EXT_METHOD *method, OSSL_TIME_SPEC *time, BIO *out, int indent) { int64_t tz; if (time->timeZone) { if (ASN1_INTEGER_get_int64(&tz, time->timeZone) != 1) return 0; if (BIO_printf(out, "%*sTimezone: UTC%+03lld:00\n", indent, "", (long long int)tz) <= 0) return 0; } if (time->notThisTime > 0) { if (BIO_printf(out, "%*sNOT this time:\n", indent, "") <= 0) return 0; } else if (BIO_printf(out, "%*sTime:\n", indent, "") <= 0) { return 0; } return i2r_OSSL_TIME_SPEC_TIME(method, time->time, out, indent + 4); } const X509V3_EXT_METHOD ossl_v3_time_specification = { NID_time_specification, X509V3_EXT_MULTILINE, ASN1_ITEM_ref(OSSL_TIME_SPEC), 0, 0, 0, 0, 0, 0, 0, 0, (X509V3_EXT_I2R)i2r_OSSL_TIME_SPEC, NULL, NULL };