You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
604 lines
17 KiB
604 lines
17 KiB
/* Copyright (C) Boris Nikolaus, Germany, 1996-1997. All rights reserved. */
|
|
/* Copyright (C) Microsoft Corporation, 1997-1998. All rights reserved. */
|
|
|
|
// lonchanc: when we call any routine in this file, we must use kernel memory,
|
|
// otheriwse, the client app should free the buffer in its entirety
|
|
// rather than free the structure piece by piece.
|
|
|
|
#include "precomp.h"
|
|
|
|
#include <math.h>
|
|
#include "ms_ut.h"
|
|
|
|
#if HAS_IEEEFP_H
|
|
#include <ieeefp.h>
|
|
#elif HAS_FLOAT_H
|
|
#include <float.h>
|
|
#endif
|
|
|
|
const ASN1octet_t ASN1double_pinf_octets[] = DBL_PINF;
|
|
const ASN1octet_t ASN1double_minf_octets[] = DBL_MINF;
|
|
|
|
/* get a positive infinite double value */
|
|
double ASN1double_pinf()
|
|
{
|
|
double val;
|
|
MyAssert(sizeof(val) == sizeof(ASN1double_pinf_octets));
|
|
CopyMemory(&val, ASN1double_pinf_octets, sizeof(ASN1double_pinf_octets));
|
|
return val;
|
|
}
|
|
|
|
/* get a negative infinite double value */
|
|
double ASN1double_minf()
|
|
{
|
|
double val;
|
|
MyAssert(sizeof(val) == sizeof(ASN1double_minf_octets));
|
|
CopyMemory(&val, ASN1double_minf_octets, sizeof(ASN1double_minf_octets));
|
|
return val;
|
|
}
|
|
|
|
/* check if double is plus infinity */
|
|
int ASN1double_ispinf(double d)
|
|
{
|
|
#if HAS_FPCLASS
|
|
return !finite(d) && fpclass(d) == FP_PINF;
|
|
#elif HAS_ISINF
|
|
return !finite(d) && isinf(d) && copysign(1.0, d) > 0.0;
|
|
#else
|
|
#error "cannot encode NaN fp values"
|
|
#endif
|
|
}
|
|
|
|
/* check if double is minus infinity */
|
|
int ASN1double_isminf(double d)
|
|
{
|
|
#if HAS_FPCLASS
|
|
return !finite(d) && fpclass(d) == FP_NINF;
|
|
#elif HAS_ISINF
|
|
return !finite(d) && isinf(d) && copysign(1.0, d) < 0.0;
|
|
#else
|
|
#error "cannot encode NaN fp values"
|
|
#endif
|
|
}
|
|
|
|
/* convert a real value into a double */
|
|
#ifdef ENABLE_REAL
|
|
double ASN1real2double(ASN1real_t *val)
|
|
{
|
|
ASN1intx_t exp;
|
|
ASN1int32_t e;
|
|
double m;
|
|
|
|
switch (val->type) {
|
|
case eReal_Normal:
|
|
m = ASN1intx2double(&val->mantissa);
|
|
if (val->base == 10) {
|
|
return m * pow(10.0, (double)ASN1intx2int32(&val->exponent));
|
|
} else {
|
|
if (val->base == 2) {
|
|
if (! ASN1intx_dup(&exp, &val->exponent))
|
|
{
|
|
return 0.0;
|
|
}
|
|
} else if (val->base == 8) {
|
|
ASN1intx_muloctet(&exp, &val->exponent, 3);
|
|
} else if (val->base == 16) {
|
|
ASN1intx_muloctet(&exp, &val->exponent, 4);
|
|
} else {
|
|
return 0.0;
|
|
}
|
|
e = ASN1intx2int32(&exp);
|
|
ASN1intx_free(&exp);
|
|
return ldexp(m, e);
|
|
}
|
|
case eReal_PlusInfinity:
|
|
return ASN1double_pinf();
|
|
case eReal_MinusInfinity:
|
|
return ASN1double_minf();
|
|
default:
|
|
return 0.0;
|
|
}
|
|
}
|
|
#endif // ENABLE_REAL
|
|
|
|
/* free a real value */
|
|
#ifdef ENABLE_REAL
|
|
void ASN1real_free(ASN1real_t *val)
|
|
{
|
|
ASN1intx_free(&val->mantissa);
|
|
ASN1intx_free(&val->exponent);
|
|
}
|
|
#endif // ENABLE_REAL
|
|
|
|
/* free a bitstring value */
|
|
void ASN1bitstring_free(ASN1bitstring_t *val)
|
|
{
|
|
if (val)
|
|
{
|
|
// lonchanc: no need to check length because zeroed decoded buffers.
|
|
MemFree(val->value);
|
|
}
|
|
}
|
|
|
|
/* free an octet string value */
|
|
void ASN1octetstring_free(ASN1octetstring_t *val)
|
|
{
|
|
if (val)
|
|
{
|
|
// lonchanc: no need to check length because zeroed decoded buffers.
|
|
MemFree(val->value);
|
|
}
|
|
}
|
|
|
|
/* free an object identifier value */
|
|
void ASN1objectidentifier_free(ASN1objectidentifier_t *val)
|
|
{
|
|
if (val)
|
|
{
|
|
// lonchanc: we allocate the entire object identifer as a chunk.
|
|
// as a result, we free it only once as a chunk.
|
|
MemFree(*val);
|
|
}
|
|
}
|
|
|
|
/* free a string value */
|
|
#ifdef ENABLE_BER
|
|
void ASN1charstring_free(ASN1charstring_t *val)
|
|
{
|
|
if (val)
|
|
{
|
|
// lonchanc: no need to check length because zeroed decoded buffers.
|
|
MemFree(val->value);
|
|
}
|
|
}
|
|
#endif // ENABLE_BER
|
|
|
|
/* free a 16 bit string value */
|
|
void ASN1char16string_free(ASN1char16string_t *val)
|
|
{
|
|
if (val)
|
|
{
|
|
// lonchanc: no need to check length because zeroed decoded buffers.
|
|
MemFree(val->value);
|
|
}
|
|
}
|
|
|
|
/* free a 32 bit string value */
|
|
#ifdef ENABLE_BER
|
|
void ASN1char32string_free(ASN1char32string_t *val)
|
|
{
|
|
if (val)
|
|
{
|
|
// lonchanc: no need to check length because zeroed decoded buffers.
|
|
MemFree(val->value);
|
|
}
|
|
}
|
|
#endif // ENABLE_BER
|
|
|
|
/* free a zero-terminated string value */
|
|
void ASN1ztcharstring_free(ASN1ztcharstring_t val)
|
|
{
|
|
MemFree(val);
|
|
}
|
|
|
|
/* free a zero-terminated 16 bit string value */
|
|
#ifdef ENABLE_BER
|
|
void ASN1ztchar16string_free(ASN1ztchar16string_t val)
|
|
{
|
|
MemFree(val);
|
|
}
|
|
#endif // ENABLE_BER
|
|
|
|
/* free a zero-terminated 32 bit string value */
|
|
#ifdef ENABLE_BER
|
|
void ASN1ztchar32string_free(ASN1ztchar32string_t val)
|
|
{
|
|
MemFree(val);
|
|
}
|
|
#endif // ENABLE_BER
|
|
|
|
/* free an external value */
|
|
#ifdef ENABLE_EXTERNAL
|
|
void ASN1external_free(ASN1external_t *val)
|
|
{
|
|
if (val)
|
|
{
|
|
switch (val->identification.o)
|
|
{
|
|
case ASN1external_identification_syntax_o:
|
|
ASN1objectidentifier_free(&val->identification.u.syntax);
|
|
break;
|
|
case ASN1external_identification_presentation_context_id_o:
|
|
break;
|
|
case ASN1external_identification_context_negotiation_o:
|
|
ASN1objectidentifier_free(
|
|
&val->identification.u.context_negotiation.transfer_syntax);
|
|
break;
|
|
}
|
|
ASN1ztcharstring_free(val->data_value_descriptor);
|
|
switch (val->data_value.o)
|
|
{
|
|
case ASN1external_data_value_notation_o:
|
|
ASN1open_free(&val->data_value.u.notation);
|
|
break;
|
|
case ASN1external_data_value_encoded_o:
|
|
ASN1bitstring_free(&val->data_value.u.encoded);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif // ENABLE_EXTERNAL
|
|
|
|
/* free an embedded pdv value */
|
|
#ifdef ENABLE_EMBEDDED_PDV
|
|
void ASN1embeddedpdv_free(ASN1embeddedpdv_t *val)
|
|
{
|
|
if (val)
|
|
{
|
|
switch (val->identification.o)
|
|
{
|
|
case ASN1embeddedpdv_identification_syntaxes_o:
|
|
ASN1objectidentifier_free(&val->identification.u.syntaxes.abstract);
|
|
ASN1objectidentifier_free(&val->identification.u.syntaxes.transfer);
|
|
break;
|
|
case ASN1embeddedpdv_identification_syntax_o:
|
|
ASN1objectidentifier_free(&val->identification.u.syntax);
|
|
break;
|
|
case ASN1embeddedpdv_identification_presentation_context_id_o:
|
|
break;
|
|
case ASN1embeddedpdv_identification_context_negotiation_o:
|
|
ASN1objectidentifier_free(
|
|
&val->identification.u.context_negotiation.transfer_syntax);
|
|
break;
|
|
case ASN1embeddedpdv_identification_transfer_syntax_o:
|
|
ASN1objectidentifier_free(&val->identification.u.transfer_syntax);
|
|
break;
|
|
case ASN1embeddedpdv_identification_fixed_o:
|
|
break;
|
|
}
|
|
switch (val->data_value.o)
|
|
{
|
|
case ASN1embeddedpdv_data_value_notation_o:
|
|
ASN1open_free(&val->data_value.u.notation);
|
|
break;
|
|
case ASN1embeddedpdv_data_value_encoded_o:
|
|
ASN1bitstring_free(&val->data_value.u.encoded);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif // ENABLE_EMBEDDED_PDV
|
|
|
|
/* free a character string value */
|
|
#ifdef ENABLE_GENERALIZED_CHAR_STR
|
|
void ASN1characterstring_free(ASN1characterstring_t *val)
|
|
{
|
|
if (val)
|
|
{
|
|
switch (val->identification.o)
|
|
{
|
|
case ASN1characterstring_identification_syntaxes_o:
|
|
ASN1objectidentifier_free(&val->identification.u.syntaxes.abstract);
|
|
ASN1objectidentifier_free(&val->identification.u.syntaxes.transfer);
|
|
break;
|
|
case ASN1characterstring_identification_syntax_o:
|
|
ASN1objectidentifier_free(&val->identification.u.syntax);
|
|
break;
|
|
case ASN1characterstring_identification_presentation_context_id_o:
|
|
break;
|
|
case ASN1characterstring_identification_context_negotiation_o:
|
|
ASN1objectidentifier_free(
|
|
&val->identification.u.context_negotiation.transfer_syntax);
|
|
break;
|
|
case ASN1characterstring_identification_transfer_syntax_o:
|
|
ASN1objectidentifier_free(&val->identification.u.transfer_syntax);
|
|
break;
|
|
case ASN1characterstring_identification_fixed_o:
|
|
break;
|
|
}
|
|
switch (val->data_value.o)
|
|
{
|
|
case ASN1characterstring_data_value_notation_o:
|
|
ASN1open_free(&val->data_value.u.notation);
|
|
break;
|
|
case ASN1characterstring_data_value_encoded_o:
|
|
ASN1octetstring_free(&val->data_value.u.encoded);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif // ENABLE_GENERALIZED_CHAR_STR
|
|
|
|
/* free an open type value */
|
|
#ifdef ENABLE_BER
|
|
void ASN1open_free(ASN1open_t *val)
|
|
{
|
|
if (val)
|
|
{
|
|
// lonchanc: no need to check length because zeroed decoded buffers.
|
|
MemFree(val->encoded);
|
|
}
|
|
}
|
|
#endif // ENABLE_BER
|
|
|
|
#ifdef ENABLE_BER
|
|
void ASN1utf8string_free(ASN1wstring_t *val)
|
|
{
|
|
if (val)
|
|
{
|
|
// lonchanc: no need to check length because zeroed decoded buffers.
|
|
MemFree(val->value);
|
|
}
|
|
}
|
|
#endif // ENABLE_BER
|
|
|
|
/* convert a generalized time value into a string */
|
|
int ASN1generalizedtime2string(char *dst, ASN1generalizedtime_t *val)
|
|
{
|
|
if (dst && val)
|
|
{
|
|
wsprintfA(dst, "%04d%02d%02d%02d%02d%02d",
|
|
val->year, val->month, val->day,
|
|
val->hour, val->minute, val->second);
|
|
if (val->millisecond) {
|
|
if (!(val->millisecond % 100))
|
|
wsprintfA(dst + 14, ".%01d", val->millisecond / 100);
|
|
else if (!(val->millisecond % 10))
|
|
wsprintfA(dst + 14, ".%02d", val->millisecond / 10);
|
|
else
|
|
wsprintfA(dst + 14, ".%03d", val->millisecond);
|
|
}
|
|
if (val->universal)
|
|
lstrcpyA(dst + lstrlenA(dst), "Z");
|
|
else if (val->diff > 0) {
|
|
if (val->diff % 60) {
|
|
wsprintfA(dst + lstrlenA(dst), "+%04d",
|
|
100 * (val->diff / 60) + (val->diff % 60));
|
|
} else {
|
|
wsprintfA(dst + lstrlenA(dst), "+%02d",
|
|
val->diff / 60);
|
|
}
|
|
} else if (val->diff < 0) {
|
|
if (val->diff % 60) {
|
|
wsprintfA(dst + lstrlenA(dst), "-%04d",
|
|
-100 * (val->diff / 60) - (val->diff % 60));
|
|
} else {
|
|
wsprintfA(dst + My_lstrlenA(dst), "-%02d",
|
|
-val->diff / 60);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* convert a utc time value into a string */
|
|
#ifdef ENABLE_BER
|
|
int ASN1utctime2string(char *dst, ASN1utctime_t *val)
|
|
{
|
|
if (dst && val)
|
|
{
|
|
wsprintfA(dst, "%02d%02d%02d%02d%02d%02d",
|
|
val->year, val->month, val->day,
|
|
val->hour, val->minute, val->second);
|
|
if (val->universal)
|
|
lstrcpyA(dst + lstrlenA(dst), "Z");
|
|
else if (val->diff > 0) {
|
|
if (val->diff % 60) {
|
|
wsprintfA(dst + lstrlenA(dst), "+%04d",
|
|
100 * (val->diff / 60) + (val->diff % 60));
|
|
} else {
|
|
wsprintfA(dst + lstrlenA(dst), "+%02d",
|
|
val->diff / 60);
|
|
}
|
|
} else if (val->diff < 0) {
|
|
if (val->diff % 60) {
|
|
wsprintfA(dst + lstrlenA(dst), "-%04d",
|
|
-100 * (val->diff / 60) - (val->diff % 60));
|
|
} else {
|
|
wsprintfA(dst + lstrlenA(dst), "-%02d",
|
|
-val->diff / 60);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif // ENABLE_BER
|
|
|
|
/* scan the fraction of a number */
|
|
static double scanfrac(char *p, char **e)
|
|
{
|
|
double ret = 0.0, d = 1.0;
|
|
|
|
while (IsDigit(*p)) {
|
|
d /= 10.0;
|
|
ret += (*p++ - '0') * d;
|
|
}
|
|
*e = p;
|
|
return ret;
|
|
}
|
|
|
|
/* convert a string into a generalized time value */
|
|
int ASN1string2generalizedtime(ASN1generalizedtime_t *dst, char *val)
|
|
{
|
|
if (dst && val)
|
|
{
|
|
int year, month, day, hour, minute, second, millisecond, diff, universal;
|
|
char *p;
|
|
double f;
|
|
|
|
millisecond = second = minute = universal = diff = 0;
|
|
if (My_lstrlenA(val) < 10)
|
|
return 0;
|
|
year = DecimalStringToUINT(val, 4);
|
|
month = DecimalStringToUINT((val+4), 2);
|
|
day = DecimalStringToUINT((val+6), 2);
|
|
hour = DecimalStringToUINT((val+8), 2);
|
|
// if (sscanf(val, "%04d%02d%02d%02d", &year, &month, &day, &hour) != 4)
|
|
// return 0;
|
|
p = val + 10;
|
|
if (*p == '.' || *p == ',') {
|
|
p++;
|
|
f = scanfrac(p, &p);
|
|
minute = (int)(f *= 60);
|
|
f -= minute;
|
|
second = (int)(f *= 60);
|
|
f -= second;
|
|
millisecond = (int)(f *= 1000);
|
|
} else if (IsDigit(*p)) {
|
|
minute = DecimalStringToUINT(p, 2);
|
|
// if (sscanf(p, "%02d", &minute) != 1)
|
|
// return 0;
|
|
p += 2;
|
|
if (*p == '.' || *p == ',') {
|
|
p++;
|
|
f = scanfrac(p, &p);
|
|
second = (int)(f *= 60);
|
|
f -= second;
|
|
millisecond = (int)(f *= 1000);
|
|
} else if (IsDigit(*p)) {
|
|
second = DecimalStringToUINT(p, 2);
|
|
// if (sscanf(p, "%02d", &second) != 1)
|
|
// return 0;
|
|
p += 2;
|
|
if (*p == '.' || *p == ',') {
|
|
p++;
|
|
f = scanfrac(p, &p);
|
|
millisecond = (int)(f *= 1000);
|
|
}
|
|
}
|
|
}
|
|
if (*p == 'Z') {
|
|
universal = 1;
|
|
p++;
|
|
} else if (*p == '+') {
|
|
f = scanfrac(p + 1, &p);
|
|
diff = (int)(f * 100) * 60 + (int)(f * 10000) % 100;
|
|
} else if (*p == '-') {
|
|
f = scanfrac(p + 1, &p);
|
|
diff = -((int)(f * 100) * 60 + (int)(f * 10000) % 100);
|
|
}
|
|
if (*p)
|
|
return 0;
|
|
dst->year = (ASN1uint16_t)year;
|
|
dst->month = (ASN1uint8_t)month;
|
|
dst->day = (ASN1uint8_t)day;
|
|
dst->hour = (ASN1uint8_t)hour;
|
|
dst->minute = (ASN1uint8_t)minute;
|
|
dst->second = (ASN1uint8_t)second;
|
|
dst->millisecond = (ASN1uint16_t)millisecond;
|
|
dst->universal = (ASN1bool_t)universal;
|
|
dst->diff = (ASN1uint16_t)diff;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* convert a string into a utc time value */
|
|
#ifdef ENABLE_BER
|
|
int ASN1string2utctime(ASN1utctime_t *dst, char *val)
|
|
{
|
|
if (dst && val)
|
|
{
|
|
char *p;
|
|
double f;
|
|
|
|
if (My_lstrlenA(val) < 10)
|
|
return 0;
|
|
|
|
p = val;
|
|
dst->year = (ASN1uint8_t) DecimalStringToUINT(p, 2);
|
|
p += 2;
|
|
dst->month = (ASN1uint8_t) DecimalStringToUINT(p, 2);
|
|
p += 2;
|
|
dst->day = (ASN1uint8_t) DecimalStringToUINT(p, 2);
|
|
p += 2;
|
|
dst->hour = (ASN1uint8_t) DecimalStringToUINT(p, 2);
|
|
p += 2;
|
|
dst->minute = (ASN1uint8_t) DecimalStringToUINT(p, 2);
|
|
p += 2;
|
|
|
|
// if (sscanf(val, "%02d%02d%02d%02d%02d",
|
|
// &year, &month, &day, &hour, &minute) != 5)
|
|
// return 0;
|
|
if (IsDigit(*p))
|
|
{
|
|
dst->second = (ASN1uint8_t) DecimalStringToUINT(p, 2);
|
|
// if (sscanf(p, "%02d", &second) != 1)
|
|
// return 0;
|
|
p += 2;
|
|
}
|
|
else
|
|
{
|
|
dst->second = 0;
|
|
}
|
|
|
|
dst->universal = 0;
|
|
dst->diff = 0;
|
|
|
|
if (*p == 'Z') {
|
|
dst->universal = 1;
|
|
p++;
|
|
} else if (*p == '+') {
|
|
f = scanfrac(p + 1, &p);
|
|
dst->diff = (int)(f * 100) * 60 + (int)(f * 10000) % 100;
|
|
} else if (*p == '-') {
|
|
f = scanfrac(p + 1, &p);
|
|
dst->diff = -((int)(f * 100) * 60 + (int)(f * 10000) % 100);
|
|
}
|
|
return ((*p) ? 0 : 1);
|
|
}
|
|
return 0;
|
|
}
|
|
#endif // ENABLE_BER
|
|
|
|
|
|
ASN1uint32_t GetObjectIdentifierCount(ASN1objectidentifier_t val)
|
|
{
|
|
ASN1uint32_t cObjIds = 0;
|
|
while (val)
|
|
{
|
|
cObjIds++;
|
|
val = val->next;
|
|
}
|
|
return cObjIds;
|
|
}
|
|
|
|
ASN1uint32_t CopyObjectIdentifier(ASN1objectidentifier_t dst, ASN1objectidentifier_t src)
|
|
{
|
|
while (dst && src)
|
|
{
|
|
dst->value = src->value;
|
|
dst = dst->next;
|
|
src = src->next;
|
|
}
|
|
return ((! dst) && (! src));
|
|
}
|
|
|
|
ASN1objectidentifier_t DecAllocObjectIdentifier(ASN1decoding_t dec, ASN1uint32_t cObjIds)
|
|
{
|
|
ASN1objectidentifier_t p, q;
|
|
ASN1uint32_t i;
|
|
p = (ASN1objectidentifier_t) DecMemAlloc(dec, cObjIds * sizeof(struct ASN1objectidentifier_s));
|
|
if (p)
|
|
{
|
|
for (q = p, i = 0; i < cObjIds-1; i++)
|
|
{
|
|
q->value = 0;
|
|
q->next = (ASN1objectidentifier_t) ((char *) q + sizeof(struct ASN1objectidentifier_s));
|
|
q = q->next;
|
|
}
|
|
q->next = NULL;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
void DecFreeObjectIdentifier(ASN1decoding_t dec, ASN1objectidentifier_t p)
|
|
{
|
|
DecMemFree(dec, p);
|
|
}
|
|
|
|
|
|
|
|
|