#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/asn1.h>
#include <openssl/asn1t.h>
#include <openssl/err.h>
#include "asn1_local.h"
#include "bytestring.h"
#include "err_local.h"
const ASN1_ITEM ASN1_BIT_STRING_it = {
.itype = ASN1_ITYPE_PRIMITIVE,
.utype = V_ASN1_BIT_STRING,
.sname = "ASN1_BIT_STRING",
};
LCRYPTO_ALIAS(ASN1_BIT_STRING_it);
ASN1_BIT_STRING *
ASN1_BIT_STRING_new(void)
{
return (ASN1_BIT_STRING *)ASN1_item_new(&ASN1_BIT_STRING_it);
}
LCRYPTO_ALIAS(ASN1_BIT_STRING_new);
void
ASN1_BIT_STRING_free(ASN1_BIT_STRING *a)
{
ASN1_item_free((ASN1_VALUE *)a, &ASN1_BIT_STRING_it);
}
LCRYPTO_ALIAS(ASN1_BIT_STRING_free);
static void
asn1_abs_clear_unused_bits(ASN1_BIT_STRING *abs)
{
abs->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
}
int
asn1_abs_set_unused_bits(ASN1_BIT_STRING *abs, uint8_t unused_bits)
{
if (unused_bits > 7)
return 0;
asn1_abs_clear_unused_bits(abs);
abs->flags |= ASN1_STRING_FLAG_BITS_LEFT | unused_bits;
return 1;
}
static int
asn1_abs_trim_trailing_zero_bits(ASN1_BIT_STRING *abs)
{
int unused_bits = 0;
while (abs->length > 0 && abs->data[abs->length - 1] == 0)
abs->length--;
if (abs->length > 0) {
uint8_t u8 = abs->data[abs->length - 1];
u8 &= 0x100 - u8;
unused_bits = 7;
if ((u8 & 0x0f) != 0)
unused_bits -= 4;
if ((u8 & 0x33) != 0)
unused_bits -= 2;
if ((u8 & 0x55) != 0)
unused_bits -= 1;
}
return asn1_abs_set_unused_bits(abs, unused_bits);
}
int
ASN1_BIT_STRING_set(ASN1_BIT_STRING *x, unsigned char *d, int len)
{
return ASN1_STRING_set(x, d, len);
}
LCRYPTO_ALIAS(ASN1_BIT_STRING_set);
int
ASN1_BIT_STRING_set_bit(ASN1_BIT_STRING *a, int n, int value)
{
int w, v, iv;
unsigned char *c;
if (a == NULL)
return 0;
if (n < 0)
return 0;
w = n / 8;
v = 1 << (7 - (n & 0x07));
iv = ~v;
if (value == 0)
v = 0;
if (a->length < w + 1 || a->data == NULL) {
if (value == 0)
return asn1_abs_trim_trailing_zero_bits(a);
if ((c = recallocarray(a->data, a->length, w + 1, 1)) == NULL) {
ASN1error(ERR_R_MALLOC_FAILURE);
return 0;
}
a->data = c;
a->length = w + 1;
}
a->data[w] = (a->data[w] & iv) | v;
return asn1_abs_trim_trailing_zero_bits(a);
}
LCRYPTO_ALIAS(ASN1_BIT_STRING_set_bit);
int
ASN1_BIT_STRING_get_bit(const ASN1_BIT_STRING *a, int n)
{
int w, v;
if (a == NULL)
return 0;
if (n < 0)
return 0;
w = n / 8;
v = 1 << (7 - (n & 0x07));
if (a->length < w + 1 || a->data == NULL)
return 0;
return (a->data[w] & v) != 0;
}
LCRYPTO_ALIAS(ASN1_BIT_STRING_get_bit);
int
i2c_ASN1_BIT_STRING(ASN1_BIT_STRING *a, unsigned char **pp)
{
int ret, j, bits, len;
unsigned char *p, *d;
if (a == NULL)
return 0;
len = a->length;
if (len > 0) {
if (a->flags & ASN1_STRING_FLAG_BITS_LEFT) {
bits = (int)a->flags & 0x07;
} else {
j = 0;
for (; len > 0; len--) {
if (a->data[len - 1])
break;
}
if (len > 0)
j = a->data[len - 1];
if (j & 0x01)
bits = 0;
else if (j & 0x02)
bits = 1;
else if (j & 0x04)
bits = 2;
else if (j & 0x08)
bits = 3;
else if (j & 0x10)
bits = 4;
else if (j & 0x20)
bits = 5;
else if (j & 0x40)
bits = 6;
else if (j & 0x80)
bits = 7;
else
bits = 0;
}
} else
bits = 0;
if (len > INT_MAX - 1)
return 0;
ret = len + 1;
if (pp == NULL)
return ret;
p = *pp;
*(p++) = (unsigned char)bits;
d = a->data;
if (len > 0) {
memcpy(p, d, len);
p += len;
p[-1] &= 0xff << bits;
}
*pp = p;
return ret;
}
int
c2i_ASN1_BIT_STRING_cbs(ASN1_BIT_STRING **out_abs, CBS *cbs)
{
ASN1_BIT_STRING *abs = NULL;
uint8_t *data = NULL;
size_t data_len = 0;
uint8_t unused_bits;
int ret = 0;
if (out_abs == NULL)
goto err;
if (*out_abs != NULL) {
ASN1_BIT_STRING_free(*out_abs);
*out_abs = NULL;
}
if (!CBS_get_u8(cbs, &unused_bits)) {
ASN1error(ASN1_R_STRING_TOO_SHORT);
goto err;
}
if (!CBS_stow(cbs, &data, &data_len))
goto err;
if (data_len > INT_MAX)
goto err;
if ((abs = ASN1_BIT_STRING_new()) == NULL)
goto err;
abs->data = data;
abs->length = (int)data_len;
data = NULL;
if (!asn1_abs_set_unused_bits(abs, unused_bits)) {
ASN1error(ASN1_R_INVALID_BIT_STRING_BITS_LEFT);
goto err;
}
if (abs->length > 0)
abs->data[abs->length - 1] &= 0xff << unused_bits;
*out_abs = abs;
abs = NULL;
ret = 1;
err:
ASN1_BIT_STRING_free(abs);
freezero(data, data_len);
return ret;
}
ASN1_BIT_STRING *
c2i_ASN1_BIT_STRING(ASN1_BIT_STRING **out_abs, const unsigned char **pp, long len)
{
ASN1_BIT_STRING *abs = NULL;
CBS content;
if (out_abs != NULL) {
ASN1_BIT_STRING_free(*out_abs);
*out_abs = NULL;
}
if (len < 0) {
ASN1error(ASN1_R_LENGTH_ERROR);
return NULL;
}
CBS_init(&content, *pp, len);
if (!c2i_ASN1_BIT_STRING_cbs(&abs, &content))
return NULL;
*pp = CBS_data(&content);
if (out_abs != NULL)
*out_abs = abs;
return abs;
}
int
i2d_ASN1_BIT_STRING(ASN1_BIT_STRING *a, unsigned char **out)
{
return ASN1_item_i2d((ASN1_VALUE *)a, out, &ASN1_BIT_STRING_it);
}
LCRYPTO_ALIAS(i2d_ASN1_BIT_STRING);
ASN1_BIT_STRING *
d2i_ASN1_BIT_STRING(ASN1_BIT_STRING **a, const unsigned char **in, long len)
{
return (ASN1_BIT_STRING *)ASN1_item_d2i((ASN1_VALUE **)a, in, len,
&ASN1_BIT_STRING_it);
}
LCRYPTO_ALIAS(d2i_ASN1_BIT_STRING);