/*
 * asn1rsa.c
 *
 *  Created on: May 16, 2013
 *      Author: ignat
 */

#include <stdint.h>
#include "CommLayerData.h"
#include "asn1.h"
#include "asn1rsa.h"
#include "log.h"

int32_t rsa_public_key_parse(const uint8_t *buf, size_t len,
                             uint8_t **modulus, uint32_t *moduluslen, 
                             uint8_t **exponent, uint32_t *exponentlen)
{
    struct asn1_hdr hdr = {0};
    const uint8_t *key_end = NULL;
    const uint8_t *pos = NULL;

    if (!buf || !len || !modulus || !exponent)
    {
        return WRONG_DATA;
    }

    if (asn1_get_next(buf, len, &hdr) || hdr.class != ASN1_CLASS_UNIVERSAL ||
        hdr.tag != ASN1_TAG_SEQUENCE)
    {
        return RSA_PARSE_ERROR;
    }

    key_end = hdr.payload + hdr.length;

    /* modulus */

    if (asn1_get_next(hdr.payload, hdr.length, &hdr) ||
        hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER)
    {
        return RSA_PARSE_ERROR;
    }

    if (0x00 == hdr.payload[0])
    {
        *modulus = (uint8_t*)hdr.payload + 1;
        *moduluslen = hdr.length - 1;
    }
    else
    {
        *modulus = (uint8_t*)hdr.payload;
        *moduluslen = hdr.length;
    }

    /* public exponent */

    pos = hdr.payload + hdr.length;
    if (asn1_get_next(pos, key_end - pos, &hdr) ||
        hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER)
    {
        goto out_parse_err;
    }

    *exponent = (uint8_t*)hdr.payload;
    *exponentlen = hdr.length;

    return NOT_ERROR;

out_parse_err:
    return RSA_PARSE_ERROR;
}


int32_t rsa_keypair_parse(const uint8_t *buf, size_t len,
                             uint8_t **priExponent, uint32_t *priExponentLen, 
                             uint8_t **modulus, uint32_t* modulusLen,
                             uint8_t **pubExponent, uint32_t *pubExponentLen)
{
    struct asn1_hdr hdr = {0};
    const uint8_t *key_end = NULL;
    const uint8_t *pos = NULL;

    if (!buf || !len || !priExponent || !priExponentLen )
    {
        return WRONG_DATA;
    }

    if (asn1_get_next(buf, len, &hdr) || hdr.class != ASN1_CLASS_UNIVERSAL ||
        hdr.tag != ASN1_TAG_SEQUENCE)
    {
        return RSA_PARSE_ERROR;
    }

    key_end = hdr.payload + hdr.length;

    /* version */

    if (asn1_get_next(hdr.payload, hdr.length, &hdr) ||
        hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER)
    {
        return RSA_PARSE_ERROR;
    }

    pos = hdr.payload + hdr.length;

    /* modulus */

    if (asn1_get_next(pos, key_end - pos, &hdr) ||
        hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER)
    {
        return RSA_PARSE_ERROR;
    }
    if (NULL != modulus && NULL != modulusLen)
    {
        if (0x00 == hdr.payload[0])
        {
            *modulus = (uint8_t*)hdr.payload + 1;
            *modulusLen = hdr.length - 1;
        }
        else
        {
            *modulus = (uint8_t*)hdr.payload;
            *modulusLen = hdr.length;
        }
    }

    pos = hdr.payload + hdr.length;

    /* public exponent */

    if (asn1_get_next(pos, key_end - pos, &hdr) ||
        hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER)
    {
        return RSA_PARSE_ERROR;
    }

    if (NULL != pubExponent && NULL != pubExponentLen)
    {
        if (0x00 == hdr.payload[0])
        {
            *pubExponent = (uint8_t*)hdr.payload + 1;
            *pubExponentLen = hdr.length - 1;
        }
        else
        {
            *pubExponent = (uint8_t*)hdr.payload;
            *pubExponentLen = hdr.length;
        }
    }
    pos = hdr.payload + hdr.length;

    /* private exponent */

    if (asn1_get_next(pos, key_end - pos, &hdr) ||
        hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER)
    {
        return RSA_PARSE_ERROR;
    }

    if (0x00 == hdr.payload[0])
    {
        *priExponent = (uint8_t*)hdr.payload + 1;
        *priExponentLen = hdr.length - 1;
    }
    else
    {
        *priExponent = (uint8_t*)hdr.payload;
        *priExponentLen = hdr.length;
    }

    pos = hdr.payload + hdr.length;

    return NOT_ERROR;
}
