/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
#include "tomcrypt_private.h"

/**
  @file der_decode_asn1_length.c
  ASN.1 DER, decode the ASN.1 Length field, Steffen Jaeckel
*/

#ifdef LTC_DER
/**
  Decode the ASN.1 Length field
  @param in       Where to read the length field from
  @param inlen    [in/out] The size of in available/read
  @param outlen   [out] The decoded ASN.1 length
  @return CRYPT_OK if successful
*/
int der_decode_asn1_length_ex(const unsigned char *in, unsigned long *inlen, unsigned long *outlen, unsigned int flags)
{
   unsigned long real_len, decoded_len, offset, i;

   LTC_ARGCHK(in    != NULL);
   LTC_ARGCHK(inlen != NULL);

   if (*inlen < 1) {
      return CRYPT_BUFFER_OVERFLOW;
   }

   real_len = in[0];

   if (real_len < 128) {
      decoded_len = real_len;
      offset = 1;
   } else {
      real_len &= 0x7F;
      if (real_len == 0) {
         return CRYPT_PK_ASN1_ERROR;
      }
      if (real_len > sizeof(decoded_len)) {
         return CRYPT_OVERFLOW;
      }
      if (real_len > (*inlen - 1)) {
         return CRYPT_BUFFER_OVERFLOW;
      }
      flags &= LTC_DER_SEQ_LEN_STRICT;
      decoded_len = 0;
      offset = 1 + real_len;
      for (i = 0; i < real_len; i++) {
         decoded_len = (decoded_len << 8) | in[1 + i];
         if ((flags == LTC_DER_SEQ_LEN_STRICT) && (decoded_len == 0)) {
            return CRYPT_PK_ASN1_ERROR;
         }
      }
      if ((flags == LTC_DER_SEQ_LEN_STRICT) && (real_len == 1) && (decoded_len < 128)) {
         return CRYPT_PK_ASN1_ERROR;
      }
   }

   if (outlen != NULL) *outlen = decoded_len;
   if (decoded_len > (*inlen - offset)) return CRYPT_OVERFLOW;
   *inlen = offset;

   return CRYPT_OK;
}

#endif
