



#include <openssl/crypto.h>
#include <openssl/bn.h>
#include "crypto/asn1_dsa.h"
#include "internal/packet.h"

#define ID_SEQUENCE 0x30
#define ID_INTEGER 0x02


int ossl_encode_der_length(WPACKET *pkt, size_t cont_len)
{
    if (cont_len > 0xffff)
        return 0; 

    if (cont_len > 0xff) {
        if (!WPACKET_put_bytes_u8(pkt, 0x82)
                || !WPACKET_put_bytes_u16(pkt, cont_len))
            return 0;
    } else {
        if (cont_len > 0x7f
                && !WPACKET_put_bytes_u8(pkt, 0x81))
            return 0;
        if (!WPACKET_put_bytes_u8(pkt, cont_len))
            return 0;
    }

    return 1;
}


int ossl_encode_der_integer(WPACKET *pkt, const BIGNUM *n)
{
    unsigned char *bnbytes;
    size_t cont_len;

    if (BN_is_negative(n))
        return 0;

    
    cont_len = BN_num_bits(n) / 8 + 1;

    if (!WPACKET_start_sub_packet(pkt)
            || !WPACKET_put_bytes_u8(pkt, ID_INTEGER)
            || !ossl_encode_der_length(pkt, cont_len)
            || !WPACKET_allocate_bytes(pkt, cont_len, &bnbytes)
            || !WPACKET_close(pkt))
        return 0;

    if (bnbytes != NULL
            && BN_bn2binpad(n, bnbytes, (int)cont_len) != (int)cont_len)
        return 0;

    return 1;
}


int ossl_encode_der_dsa_sig(WPACKET *pkt, const BIGNUM *r, const BIGNUM *s)
{
    WPACKET tmppkt, *dummypkt;
    size_t cont_len;
    int isnull = WPACKET_is_null_buf(pkt);

    if (!WPACKET_start_sub_packet(pkt))
        return 0;

    if (!isnull) {
        if (!WPACKET_init_null(&tmppkt, 0))
            return 0;
        dummypkt = &tmppkt;
    } else {
        
        dummypkt = pkt;
    }

    
    if (!ossl_encode_der_integer(dummypkt, r)
            || !ossl_encode_der_integer(dummypkt, s)
            || !WPACKET_get_length(dummypkt, &cont_len)
            || (!isnull && !WPACKET_finish(dummypkt))) {
        if (!isnull)
            WPACKET_cleanup(dummypkt);
        return 0;
    }

    
    if (!WPACKET_put_bytes_u8(pkt, ID_SEQUENCE)
            || !ossl_encode_der_length(pkt, cont_len)
               
            || (!isnull && !ossl_encode_der_integer(pkt, r))
            || (!isnull && !ossl_encode_der_integer(pkt, s))
            || !WPACKET_close(pkt))
        return 0;

    return 1;
}


int ossl_decode_der_length(PACKET *pkt, PACKET *subpkt)
{
    unsigned int byte;

    if (!PACKET_get_1(pkt, &byte))
        return 0;

    if (byte < 0x80)
        return PACKET_get_sub_packet(pkt, subpkt, (size_t)byte);
    if (byte == 0x81)
        return PACKET_get_length_prefixed_1(pkt, subpkt);
    if (byte == 0x82)
        return PACKET_get_length_prefixed_2(pkt, subpkt);

    
    return 0;
}


int ossl_decode_der_integer(PACKET *pkt, BIGNUM *n)
{
    PACKET contpkt, tmppkt;
    unsigned int tag, tmp;

    
    if (!PACKET_get_1(pkt, &tag)
            || tag != ID_INTEGER
            || !ossl_decode_der_length(pkt, &contpkt))
        return 0;

    
    tmppkt = contpkt;
    
    if (!PACKET_get_1(&tmppkt, &tmp)
            || (tmp & 0x80) != 0)
        return 0;
    
    if (PACKET_remaining(&tmppkt) > 0 && tmp == 0) {
        if (!PACKET_get_1(&tmppkt, &tmp)
                || (tmp & 0x80) == 0)
            return 0;
    }

    if (BN_bin2bn(PACKET_data(&contpkt),
                  (int)PACKET_remaining(&contpkt), n) == NULL)
        return 0;

    return 1;
}


size_t ossl_decode_der_dsa_sig(BIGNUM *r, BIGNUM *s,
                               const unsigned char **ppin, size_t len)
{
    size_t consumed;
    PACKET pkt, contpkt;
    unsigned int tag;

    if (!PACKET_buf_init(&pkt, *ppin, len)
            || !PACKET_get_1(&pkt, &tag)
            || tag != ID_SEQUENCE
            || !ossl_decode_der_length(&pkt, &contpkt)
            || !ossl_decode_der_integer(&contpkt, r)
            || !ossl_decode_der_integer(&contpkt, s)
            || PACKET_remaining(&contpkt) != 0)
        return 0;

    consumed = PACKET_data(&pkt) - *ppin;
    *ppin += consumed;
    return consumed;
}
