From 2bd517307c13ce579d7d7388aab93febee8af0f4 Mon Sep 17 00:00:00 2001 From: Steffen Jaeckel Date: Thu, 30 Mar 2017 18:58:21 +0200 Subject: [PATCH] add GeneralizedTime DER en-/decode --- src/headers/tomcrypt.h | 2 +- src/headers/tomcrypt_pk.h | 21 +++ src/pk/asn1/der/choice/der_decode_choice.c | 9 ++ .../der_decode_generalizedtime.c | 131 ++++++++++++++++++ .../der_encode_generalizedtime.c | 103 ++++++++++++++ .../der_length_generalizedtime.c | 53 +++++++ .../der/sequence/der_decode_sequence_ex.c | 8 ++ .../der/sequence/der_decode_sequence_flexi.c | 19 +++ .../der/sequence/der_decode_sequence_multi.c | 2 + .../der/sequence/der_encode_sequence_ex.c | 16 +++ .../der/sequence/der_encode_sequence_multi.c | 2 + .../asn1/der/sequence/der_length_sequence.c | 7 + src/pk/asn1/der/set/der_encode_set.c | 1 + testprof/der_tests.c | 13 ++ 14 files changed, 386 insertions(+), 1 deletion(-) create mode 100644 src/pk/asn1/der/generalizedtime/der_decode_generalizedtime.c create mode 100644 src/pk/asn1/der/generalizedtime/der_encode_generalizedtime.c create mode 100644 src/pk/asn1/der/generalizedtime/der_length_generalizedtime.c diff --git a/src/headers/tomcrypt.h b/src/headers/tomcrypt.h index d38a7c7e..6f661c79 100644 --- a/src/headers/tomcrypt.h +++ b/src/headers/tomcrypt.h @@ -55,7 +55,7 @@ enum { CRYPT_FILE_NOTFOUND, /* File Not Found */ CRYPT_PK_INVALID_TYPE, /* Invalid type of PK key */ - CRYPT_PK_INVALID_SYSTEM,/* Invalid PK system specified */ + CRYPT_OVERFLOW, /* An overflow of a value was prevented */ CRYPT_PK_DUP, /* Duplicate key already in key ring */ CRYPT_PK_NOT_FOUND, /* Key not found in keyring */ CRYPT_PK_INVALID_SIZE, /* Invalid size input for PK parameters */ diff --git a/src/headers/tomcrypt_pk.h b/src/headers/tomcrypt_pk.h index be19bad2..a39f5b80 100644 --- a/src/headers/tomcrypt_pk.h +++ b/src/headers/tomcrypt_pk.h @@ -475,6 +475,8 @@ typedef enum ltc_asn1_type_ { LTC_ASN1_TELETEX_STRING, LTC_ASN1_CONSTRUCTED, LTC_ASN1_CONTEXT_SPECIFIC, + /* 20 */ + LTC_ASN1_GENERALIZEDTIME, } ltc_asn1_type; /** A LTC ASN.1 list type */ @@ -662,6 +664,25 @@ int der_decode_utctime(const unsigned char *in, unsigned long *inlen, int der_length_utctime(ltc_utctime *utctime, unsigned long *outlen); +/* GeneralizedTime */ +typedef struct { + unsigned YYYY, /* year */ + MM, /* month */ + DD, /* day */ + hh, /* hour */ + mm, /* minute */ + ss, /* second */ + fs; /* fractional seconds */ +} ltc_generalizedtime; + +int der_encode_generalizedtime(ltc_generalizedtime *gtime, + unsigned char *out, unsigned long *outlen); + +int der_decode_generalizedtime(const unsigned char *in, unsigned long *inlen, + ltc_generalizedtime *out); + +int der_length_generalizedtime(ltc_generalizedtime *gtime, unsigned long *outlen); + #endif diff --git a/src/pk/asn1/der/choice/der_decode_choice.c b/src/pk/asn1/der/choice/der_decode_choice.c index e90e67af..eb715130 100644 --- a/src/pk/asn1/der/choice/der_decode_choice.c +++ b/src/pk/asn1/der/choice/der_decode_choice.c @@ -186,6 +186,15 @@ int der_decode_choice(const unsigned char *in, unsigned long *inlen, } break; + case LTC_ASN1_GENERALIZEDTIME: + z = *inlen; + if (der_decode_generalizedtime(in, &z, data) == CRYPT_OK) { + list[x].used = 1; + *inlen = z; + return CRYPT_OK; + } + break; + case LTC_ASN1_SET: case LTC_ASN1_SETOF: case LTC_ASN1_SEQUENCE: diff --git a/src/pk/asn1/der/generalizedtime/der_decode_generalizedtime.c b/src/pk/asn1/der/generalizedtime/der_decode_generalizedtime.c new file mode 100644 index 00000000..0fd55791 --- /dev/null +++ b/src/pk/asn1/der/generalizedtime/der_decode_generalizedtime.c @@ -0,0 +1,131 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "tomcrypt.h" + +/** + @file der_decode_generalizedtime.c + ASN.1 DER, decode a GeneralizedTime, Steffen Jaeckel + Based on der_decode_utctime.c +*/ + +#ifdef LTC_DER + +static int char_to_int(unsigned char x) +{ + switch (x) { + case '0': return 0; + case '1': return 1; + case '2': return 2; + case '3': return 3; + case '4': return 4; + case '5': return 5; + case '6': return 6; + case '7': return 7; + case '8': return 8; + case '9': return 9; + } + return 100; +} + +#define DECODE_V(y, max) do {\ + y = char_to_int(buf[x])*10 + char_to_int(buf[x+1]); \ + if (y >= max) return CRYPT_INVALID_PACKET; \ + x += 2; \ +} while(0) + +#define DECODE_V4(y, max) do {\ + y = char_to_int(buf[x])*1000 + char_to_int(buf[x+1])*100 + char_to_int(buf[x+2])*10 + char_to_int(buf[x+3]); \ + if (y >= max) return CRYPT_INVALID_PACKET; \ + x += 4; \ +} while(0) + +/** + Decodes a Generalized time structure in DER format (reads all 6 valid encoding formats) + @param in Input buffer + @param inlen Length of input buffer in octets + @param out [out] Destination of Generalized time structure + @return CRYPT_OK if successful +*/ +int der_decode_generalizedtime(const unsigned char *in, unsigned long *inlen, + ltc_generalizedtime *out) +{ + unsigned char buf[32]; + unsigned long x; + int y; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(inlen != NULL); + LTC_ARGCHK(out != NULL); + + /* check header */ + if (*inlen < 2UL || (in[1] >= sizeof(buf)) || ((in[1] + 2UL) > *inlen)) { + return CRYPT_INVALID_PACKET; + } + + /* decode the string */ + for (x = 0; x < in[1]; x++) { + y = der_ia5_value_decode(in[x+2]); + if (y == -1) { + return CRYPT_INVALID_PACKET; + } + if (!((y >= '0' && y <= '9') || y == 'Z')) { + return CRYPT_INVALID_PACKET; + } + buf[x] = y; + } + *inlen = 2 + x; + + if (x < 15) { + return CRYPT_INVALID_PACKET; + } + + /* possible encodings are +YYYYMMDDhhmmssZ +YYYYMMDDhhmmss.[0-9]*Z + + So let's do a trivial decode upto [including] ss + */ + + x = 0; + DECODE_V4(out->YYYY, 10000); + DECODE_V(out->MM, 13); + DECODE_V(out->DD, 32); + DECODE_V(out->hh, 24); + DECODE_V(out->mm, 60); + DECODE_V(out->ss, 60); + + /* clear fractional seconds info */ + out->fs = 0; + + /* now is it Z or . */ + if (buf[x] == 'Z') { + return CRYPT_OK; + } else if (buf[x] == '.') { + x++; + while (buf[x] != 'Z') { + unsigned fs = out->fs; + if (x >= sizeof(buf)) return CRYPT_INVALID_PACKET; + out->fs *= 10; + out->fs += char_to_int(buf[x]); + if (fs < out->fs) return CRYPT_OVERFLOW; + x++; + } + return CRYPT_OK; + } else { + return CRYPT_INVALID_PACKET; + } +} + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ diff --git a/src/pk/asn1/der/generalizedtime/der_encode_generalizedtime.c b/src/pk/asn1/der/generalizedtime/der_encode_generalizedtime.c new file mode 100644 index 00000000..4852684c --- /dev/null +++ b/src/pk/asn1/der/generalizedtime/der_encode_generalizedtime.c @@ -0,0 +1,103 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "tomcrypt.h" + +/** + @file der_encode_utctime.c + ASN.1 DER, encode a GeneralizedTime, Steffen Jaeckel + Based on der_encode_utctime.c +*/ + +#ifdef LTC_DER + +static const char * const baseten = "0123456789"; + +#define STORE_V(y) do {\ + out[x++] = der_ia5_char_encode(baseten[(y/10) % 10]); \ + out[x++] = der_ia5_char_encode(baseten[y % 10]); \ +} while(0) + +#define STORE_V4(y) do {\ + out[x++] = der_ia5_char_encode(baseten[(y/1000) % 10]); \ + out[x++] = der_ia5_char_encode(baseten[(y/100) % 10]); \ + out[x++] = der_ia5_char_encode(baseten[(y/10) % 10]); \ + out[x++] = der_ia5_char_encode(baseten[y % 10]); \ +} while(0) + +/** + Encodes a Generalized time structure in DER format + @param utctime The UTC time structure to encode + @param out The destination of the DER encoding of the UTC time structure + @param outlen [in/out] The length of the DER encoding + @return CRYPT_OK if successful +*/ +int der_encode_generalizedtime(ltc_generalizedtime *gtime, + unsigned char *out, unsigned long *outlen) +{ + unsigned long x, tmplen; + int err; + + LTC_ARGCHK(gtime != NULL); + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + + if ((err = der_length_generalizedtime(gtime, &tmplen)) != CRYPT_OK) { + return err; + } + if (tmplen > *outlen) { + *outlen = tmplen; + return CRYPT_BUFFER_OVERFLOW; + } + + /* store header */ + out[0] = 0x18; + + /* store values */ + x = 2; + STORE_V4(gtime->YYYY); + STORE_V(gtime->MM); + STORE_V(gtime->DD); + STORE_V(gtime->hh); + STORE_V(gtime->mm); + STORE_V(gtime->ss); + + if (gtime->fs) { + unsigned long div; + unsigned fs = gtime->fs; + unsigned len = 0; + out[x++] = der_ia5_char_encode('.'); + div = 1; + do { + fs /= 10; + div *= 10; + len++; + } while(fs != 0); + while (len-- > 1) { + out[x++] = der_ia5_char_encode(baseten[(gtime->fs/div) % 10]); + div /= 10; + } + out[x++] = der_ia5_char_encode(baseten[gtime->fs % 10]); + } + out[x++] = der_ia5_char_encode('Z'); + + /* store length */ + out[1] = (unsigned char)(x - 2); + + /* all good let's return */ + *outlen = x; + return CRYPT_OK; +} + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ diff --git a/src/pk/asn1/der/generalizedtime/der_length_generalizedtime.c b/src/pk/asn1/der/generalizedtime/der_length_generalizedtime.c new file mode 100644 index 00000000..eff3948b --- /dev/null +++ b/src/pk/asn1/der/generalizedtime/der_length_generalizedtime.c @@ -0,0 +1,53 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtom.org + */ +#include "tomcrypt.h" + +/** + @file der_length_utctime.c + ASN.1 DER, get length of GeneralizedTime, Steffen Jaeckel + Based on der_length_utctime.c +*/ + +#ifdef LTC_DER + +/** + Gets length of DER encoding of GeneralizedTime + @param utctime The UTC time structure to get the size of + @param outlen [out] The length of the DER encoding + @return CRYPT_OK if successful +*/ +int der_length_generalizedtime(ltc_generalizedtime *gtime, unsigned long *outlen) +{ + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(gtime != NULL); + + if (gtime->fs == 0) { + /* we encode as YYYYMMDDhhmmssZ */ + *outlen = 2 + 15; + } else { + /* we encode as YYYYMMDDhhmmss.fsZ */ + unsigned long len = 2 + 17; + unsigned fs = gtime->fs; + do { + fs /= 10; + len++; + } while(fs != 0); + *outlen = len; + } + + return CRYPT_OK; +} + +#endif + +/* $Source$ */ +/* $Revision$ */ +/* $Date$ */ diff --git a/src/pk/asn1/der/sequence/der_decode_sequence_ex.c b/src/pk/asn1/der/sequence/der_decode_sequence_ex.c index 8a362b7a..26e0e712 100644 --- a/src/pk/asn1/der/sequence/der_decode_sequence_ex.c +++ b/src/pk/asn1/der/sequence/der_decode_sequence_ex.c @@ -244,6 +244,14 @@ int der_decode_sequence_ex(const unsigned char *in, unsigned long inlen, } break; + case LTC_ASN1_GENERALIZEDTIME: + z = inlen; + if ((err = der_decode_generalizedtime(in + x, &z, data)) != CRYPT_OK) { + if (!ordered) { continue; } + goto LBL_ERR; + } + break; + case LTC_ASN1_SET: z = inlen; if ((err = der_decode_set(in + x, z, data, size)) != CRYPT_OK) { diff --git a/src/pk/asn1/der/sequence/der_decode_sequence_flexi.c b/src/pk/asn1/der/sequence/der_decode_sequence_flexi.c index 5435381c..d76f4036 100644 --- a/src/pk/asn1/der/sequence/der_decode_sequence_flexi.c +++ b/src/pk/asn1/der/sequence/der_decode_sequence_flexi.c @@ -347,6 +347,25 @@ int der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc } break; + case 0x18: + l->type = LTC_ASN1_GENERALIZEDTIME; + l->size = len; + + if ((l->data = XCALLOC(1, sizeof(ltc_generalizedtime))) == NULL) { + err = CRYPT_MEM; + goto error; + } + + if ((err = der_decode_generalizedtime(in, &len, l->data)) != CRYPT_OK) { + goto error; + } + + if ((err = der_length_generalizedtime(l->data, &len)) != CRYPT_OK) { + goto error; + } + + break; + case 0x20: /* Any CONSTRUCTED element that is neither SEQUENCE nor SET */ case 0x30: /* SEQUENCE */ case 0x31: /* SET */ diff --git a/src/pk/asn1/der/sequence/der_decode_sequence_multi.c b/src/pk/asn1/der/sequence/der_decode_sequence_multi.c index 8fa4ae0e..ba234127 100644 --- a/src/pk/asn1/der/sequence/der_decode_sequence_multi.c +++ b/src/pk/asn1/der/sequence/der_decode_sequence_multi.c @@ -69,6 +69,7 @@ int der_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...) case LTC_ASN1_CHOICE: case LTC_ASN1_RAW_BIT_STRING: case LTC_ASN1_TELETEX_STRING: + case LTC_ASN1_GENERALIZEDTIME: ++x; break; @@ -121,6 +122,7 @@ int der_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...) case LTC_ASN1_CHOICE: case LTC_ASN1_RAW_BIT_STRING: case LTC_ASN1_TELETEX_STRING: + case LTC_ASN1_GENERALIZEDTIME: LTC_SET_ASN1(list, x++, type, data, size); break; /* coverity[dead_error_line] */ diff --git a/src/pk/asn1/der/sequence/der_encode_sequence_ex.c b/src/pk/asn1/der/sequence/der_encode_sequence_ex.c index 0f17118b..e7332073 100644 --- a/src/pk/asn1/der/sequence/der_encode_sequence_ex.c +++ b/src/pk/asn1/der/sequence/der_encode_sequence_ex.c @@ -126,6 +126,13 @@ int der_encode_sequence_ex(ltc_asn1_list *list, unsigned long inlen, y += x; break; + case LTC_ASN1_GENERALIZEDTIME: + if ((err = der_length_generalizedtime(data, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + case LTC_ASN1_SET: case LTC_ASN1_SETOF: case LTC_ASN1_SEQUENCE: @@ -307,6 +314,15 @@ int der_encode_sequence_ex(ltc_asn1_list *list, unsigned long inlen, *outlen -= z; break; + case LTC_ASN1_GENERALIZEDTIME: + z = *outlen; + if ((err = der_encode_generalizedtime(data, out + x, &z)) != CRYPT_OK) { + goto LBL_ERR; + } + x += z; + *outlen -= z; + break; + case LTC_ASN1_SET: z = *outlen; if ((err = der_encode_set(data, size, out + x, &z)) != CRYPT_OK) { diff --git a/src/pk/asn1/der/sequence/der_encode_sequence_multi.c b/src/pk/asn1/der/sequence/der_encode_sequence_multi.c index cf17f7fa..3bd76bfc 100644 --- a/src/pk/asn1/der/sequence/der_encode_sequence_multi.c +++ b/src/pk/asn1/der/sequence/der_encode_sequence_multi.c @@ -68,6 +68,7 @@ int der_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...) case LTC_ASN1_SET: case LTC_ASN1_SETOF: case LTC_ASN1_RAW_BIT_STRING: + case LTC_ASN1_GENERALIZEDTIME: ++x; break; @@ -120,6 +121,7 @@ int der_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...) case LTC_ASN1_SET: case LTC_ASN1_SETOF: case LTC_ASN1_RAW_BIT_STRING: + case LTC_ASN1_GENERALIZEDTIME: LTC_SET_ASN1(list, x++, type, data, size); break; diff --git a/src/pk/asn1/der/sequence/der_length_sequence.c b/src/pk/asn1/der/sequence/der_length_sequence.c index c7fec541..99e427a3 100644 --- a/src/pk/asn1/der/sequence/der_length_sequence.c +++ b/src/pk/asn1/der/sequence/der_length_sequence.c @@ -122,6 +122,13 @@ int der_length_sequence(ltc_asn1_list *list, unsigned long inlen, y += x; break; + case LTC_ASN1_GENERALIZEDTIME: + if ((err = der_length_generalizedtime(data, &x)) != CRYPT_OK) { + goto LBL_ERR; + } + y += x; + break; + case LTC_ASN1_UTF8_STRING: if ((err = der_length_utf8_string(data, size, &x)) != CRYPT_OK) { goto LBL_ERR; diff --git a/src/pk/asn1/der/set/der_encode_set.c b/src/pk/asn1/der/set/der_encode_set.c index ae87697b..75de2345 100644 --- a/src/pk/asn1/der/set/der_encode_set.c +++ b/src/pk/asn1/der/set/der_encode_set.c @@ -34,6 +34,7 @@ static int ltc_to_asn1(ltc_asn1_type v) case LTC_ASN1_TELETEX_STRING: return 0x14; case LTC_ASN1_IA5_STRING: return 0x16; case LTC_ASN1_UTCTIME: return 0x17; + case LTC_ASN1_GENERALIZEDTIME: return 0x18; case LTC_ASN1_SEQUENCE: return 0x30; case LTC_ASN1_SET: case LTC_ASN1_SETOF: return 0x31; diff --git a/testprof/der_tests.c b/testprof/der_tests.c index dcaf1b2a..420ac007 100644 --- a/testprof/der_tests.c +++ b/testprof/der_tests.c @@ -335,6 +335,19 @@ static void _der_tests_print_flexi(ltc_asn1_list* l, unsigned int level) text = buf; } break; + case LTC_ASN1_GENERALIZEDTIME: + name = "GENERALIZED TIME"; + { + ltc_generalizedtime* gt = l->data; + if(gt->fs) + snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d:%02d:%02d.%02dZ", + gt->YYYY, gt->MM, gt->DD, gt->hh, gt->mm, gt->ss, gt->fs); + else + snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d:%02d:%02dZ", + gt->YYYY, gt->MM, gt->DD, gt->hh, gt->mm, gt->ss); + text = buf; + } + break; case LTC_ASN1_CHOICE: name = "CHOICE"; break;