/**
 * @file       drk_parser.c
 * @brief      Find certificates in DRK TLV structure. Find RSA parameters
 *             in DRK private key ASN.1 structure.
 * @author     Oleksandr Fadieiev (o.fadieiev@samsung.com)
 * @version    1.0
 * @date       Created April 20, 2017
 * @copyright  In Samsung Ukraine R&D Center (SURC) under a contract between
 * @par        LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) and
 * @par        "Samsung Electronics Co", Ltd (Seoul, Rersa_cert of Korea)
 * @par        Copyright: (c) Samsung Electronics Co, Ltd 2017. All rights reserved.
 */
#include <drk_parser.h>
#include "tlv.h"
#include "der.h"

enum {
  kDrkRsaCertTag = 1,
  kDrkIvTag = 2,
  kDrkKeyTag = 3,
  kDrkTlNameTag = 4,
  kDrkAttrsTag = 5
};

enum {
  kRsaPositionVersion = 0,
  kRsaPositionModulus,
  kRsaPositionExponent,
  kRsaPositionPrivateKey,
  kRsaPositionPrime1,
  kRsaPositionPrime2,
  kRsaPositionExponent1,
  kRsaPositionExponent2,
  kRsaPositionCoefficient,
  kRsaPositionMaxCount
};

DrkResult DrkParserGetCertificates(const void *certificates,
                                   size_t certificates_size,
                                   const void **drk_cert, size_t *drk_cert_size,
                                   const void **rsa_cert, size_t *rsa_cert_size,
                                   const void **rsa_priv, size_t *rsa_priv_size) {
  // DRK blob has a special format. Need to parse TLV.
  //    | 0x01 | 2 bytes of length | x.509 DRK cert | 0x01 | 2 bytes of length | x.509 RSA cert ->
  // -> | 0x03 | 2 bytes of length | asn.1 RSA priv | 0x04 | 2 bytes of length | TID
  //
  const TlvHeader *begin = (const TlvHeader *)certificates;
  const TlvHeader *next = begin;
  const void *end = (uint8_t *)begin + certificates_size;

  if (!certificates || certificates_size == 0 || !drk_cert || !drk_cert_size ||
          !rsa_cert || !rsa_cert_size || !rsa_priv || !rsa_priv_size) {
    return kDrkResultErrorBadParameters;
  }

  // Check the very 1st TLV length. TlvNext fails when TLV is invalid.
  // For example, length value must not be greater than actual size of buffer.
  if (!TlvNext(begin, end)) {
    return kDrkResultErrorShortBuffer;
  }

  *drk_cert = NULL;
  *drk_cert_size = 0;

  // Find the first sequential "0x01" TLV tags from the beginning: "RSA cert".
  // | 0x01 | 2 bytes of length | x.509 DRK cert | 0x01 | 2 bytes of length | x.509 RSA cert | ...
  if (next->tag == kDrkRsaCertTag) {
    *drk_cert = next->value;
    *drk_cert_size = TlvLength(next);
  } else {
      return kDrkResultErrorGeneric;
  }

  if (*drk_cert == NULL || *drk_cert_size == 0) {
    return kDrkResultErrorGeneric;
  }

  next = TlvNext(next, end);

  *rsa_cert = NULL;
  *rsa_cert_size = 0;

  // Find the second sequential "0x01" TLV tags from the beginning: "RSA cert".
  if (next->tag == kDrkRsaCertTag) {
    *rsa_cert = next->value;
    *rsa_cert_size = TlvLength(next);
  } else {
    return kDrkResultErrorGeneric;
  }

  if (*rsa_cert == NULL || *rsa_cert_size == 0) {
    return kDrkResultErrorGeneric;
  }

  *rsa_priv_size = 0;
  next = begin;

  // Find only one "0x03" TLV tag. There is a wrapped (protected) private key.
  //  ... | 0x03 | 2 bytes of length | asn.1 RSA priv | ...
  do {
    if (next->tag == kDrkKeyTag) {
      *rsa_priv = next->value;
      *rsa_priv_size = TlvLength(next);
      break;
    }
  } while ((next = TlvNext(next, end)));

  if (*rsa_priv_size == 0) {
    return kDrkResultErrorGeneric;
  }

  return kDrkResultSuccess;
}

DrkResult DrkParserGetRsaParameters(const void *private_cert,
                                    size_t private_cert_size,
                                    const void **n, size_t *n_size,
                                    const void **e, size_t *e_size,
                                    const void **d, size_t *d_size,
                                    const void **prime1, size_t *prime1_size,
                                    const void **prime2, size_t *prime2_size,
                                    const void **exp1, size_t *exp1_size,
                                    const void **exp2, size_t *exp2_size,
                                    const void **coef, size_t *coef_size) {
  if (!private_cert || !n || !n_size || !e || !e_size || !d || !d_size ||
      !prime1 || !prime1_size || !prime2 || !prime2_size || !exp1 ||
      !exp1_size || !exp2 || !exp2_size || !coef || !coef_size) {
    return kDrkResultErrorBadParameters;
  }

  const DerHeader *next = (const DerHeader *)private_cert;
  const void *end = private_cert + private_cert_size;

  int i = 0;

  // Format of the private key certificate is the following:
  //   SEQUENCE {
  //     version INTEGER,
  //     modulus INTEGER, -- n
  //     exponent INTEGER, -- e
  //     private key INTEGER, -- d
  //     ....
  //   }
  while (i < kRsaPositionMaxCount && next) {
    if (DerTag(next) & kDerConstructed) {
      // Constructed type: jump inside the value.
      next = (const DerHeader *)DerValue(next);
    } else {
      // Simple type: jump over value in the end.

      // Integer is expected.
      if (DerTag(next) != kDerInteger) {
        return kDrkResultErrorGeneric;
      }

      switch (i) {
        case kRsaPositionVersion: {
          break;
        }
        case kRsaPositionModulus: {
          *n = DerValue(next);
          *n_size = DerLength(next);
          break;
        }
        case kRsaPositionExponent: {
          *e = DerValue(next);
          *e_size = DerLength(next);
          break;
        }
        case kRsaPositionPrivateKey: {
          *d = DerValue(next);
          *d_size = DerLength(next);
          break;
        }
        case kRsaPositionPrime1: {
          *prime1 = DerValue(next);
          *prime1_size = DerLength(next);
          break;
        }
        case kRsaPositionPrime2: {
          *prime2 = DerValue(next);
          *prime2_size = DerLength(next);
          break;
        }
        case kRsaPositionExponent1: {
          *exp1 = DerValue(next);
          *exp1_size = DerLength(next);
          break;
        }
        case kRsaPositionExponent2: {
          *exp2 = DerValue(next);
          *exp2_size = DerLength(next);
          break;
        }
        case kRsaPositionCoefficient: {
          *coef = DerValue(next);
          *coef_size = DerLength(next);
          break;
        }
      }

      // Jump over the simple type.
      next = (const DerHeader *)DerNext(next, end);
      i++;
    }
  }

  return kDrkResultSuccess;
}
