/* LibTomCrypt, modular cryptographic library -- Tom St Denis
 *
 * LibTomCrypt is a library that provides various cryptographic
 * algorithms in a highly modular and flexible manner.
 *
 * The library is free for all purposes without any express
 * guarantee it works.
 *
 * Tom St Denis, tomstdenis@gmail.com, http://libtom.org
 */
#include "tomcrypt.h"

/**
  @file rsa_import_pkcs8.c
  Import a PKCS RSA key
*/

#ifdef LTC_MRSA

/* Public-Key Cryptography Standards (PKCS) #8:
 * Private-Key Information Syntax Specification Version 1.2
 * https://tools.ietf.org/html/rfc5208
 *
 * PrivateKeyInfo ::= SEQUENCE {
 *      version                   Version,
 *      privateKeyAlgorithm       PrivateKeyAlgorithmIdentifier,
 *      privateKey                PrivateKey,
 *      attributes           [0]  IMPLICIT Attributes OPTIONAL }
 * where:
 * - Version ::= INTEGER
 * - PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
 * - PrivateKey ::= OCTET STRING
 * - Attributes ::= SET OF Attribute
 * 
 * EncryptedPrivateKeyInfo ::= SEQUENCE {
 *        encryptionAlgorithm  EncryptionAlgorithmIdentifier,
 *        encryptedData        EncryptedData }
 * where:
 * - EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
 * - EncryptedData ::= OCTET STRING
 */

/*
 * This PKCS12 key derivation code comes from BouncyCastle.
 *
 * @param idByte         1 == key, 2 == iv
 * @param n              keysize or ivsize
 * @param salt           8 byte salt
 * @param password       password
 * @param iterationCount iteration-count
 * @param md             The message digest to use
 * @return byte[] the derived key
 */

/*
pkcs12(int idByte, int n, byte[] salt, byte[] password, int iterationCount, MessageDigest md)
*/

int rsa_import_pkcs8(unsigned char *in, unsigned long inlen, rsa_key *key)
{
   int            err;
   void           *zero, *iter;
   unsigned char  *buf1=NULL, *buf2=NULL;
   unsigned long  buf1len, buf2len;
   unsigned long  oid[16];
   oid_st         rsaoid; 
   ltc_asn1_list  alg_seq[2], top_seq[3];
   ltc_asn1_list  alg_seq_e[2], key_seq_e[2], top_seq_e[2];
   unsigned char  *decrypted=NULL;
   unsigned long  decryptedlen;
 
   LTC_ARGCHK(in          != NULL);
   LTC_ARGCHK(key         != NULL);
   LTC_ARGCHK(ltc_mp.name != NULL);

   /* get RSA alg oid */
   err = pk_get_oid(PKA_RSA, &rsaoid);
   if (err != CRYPT_OK) { return err; }
   
   /* alloc buffers */
   buf1len = 10000; /* XXX-TODO LTC_DER_MAX_PUBKEY_SIZE*8 nebo inlen*/
   buf1 = XCALLOC(1, buf1len);
   if (buf1 == NULL) { err = CRYPT_MEM; goto LBL_FREE; }
   buf2len = 10000; /* XXX-TODO LTC_DER_MAX_PUBKEY_SIZE*8 nebo inlen */
   buf2 = XCALLOC(1, buf2len);
   if (buf2 == NULL) { err = CRYPT_MEM; goto LBL_FREE; }

   /* init key */
   err = mp_init_multi(&key->e, &key->d, &key->N, &key->dQ, &key->dP, &key->qP, &key->p, &key->q, &zero, &iter, NULL);
   if (err != CRYPT_OK) { return err; }

   /* try to decode encrypted priv key */
   LTC_SET_ASN1(key_seq_e, 0, LTC_ASN1_OCTET_STRING, buf1, buf1len);
   LTC_SET_ASN1(key_seq_e, 1, LTC_ASN1_INTEGER, iter, 1UL);
   LTC_SET_ASN1(alg_seq_e, 0, LTC_ASN1_OBJECT_IDENTIFIER, oid, 16UL);
   LTC_SET_ASN1(alg_seq_e, 1, LTC_ASN1_SEQUENCE, key_seq_e, 2UL);
   LTC_SET_ASN1(top_seq_e, 0, LTC_ASN1_SEQUENCE, alg_seq_e, 2UL);
   LTC_SET_ASN1(top_seq_e, 1, LTC_ASN1_OCTET_STRING, buf2, buf2len);  
   err=der_decode_sequence(in, inlen, top_seq_e, 2UL);
   if (err == CRYPT_OK) {
     /* unsigned long icount = mp_get_int(iter); */
     /* XXX: TODO */
     /* fprintf(stderr, "XXX-DEBUG: gonna decrypt: iter=%ld salt.len=%ld encdata.len=%ld\n", icount, key_seq_e[0].size, top_seq_e[1].size); */
     err = CRYPT_PK_INVALID_TYPE;
     goto LBL_ERR;
   }
   else {
     decrypted    = in;
     decryptedlen = inlen;
   }

   /* try to decode unencrypted priv key */
   LTC_SET_ASN1(alg_seq, 0, LTC_ASN1_OBJECT_IDENTIFIER, oid, 16UL);
   LTC_SET_ASN1(alg_seq, 1, LTC_ASN1_NULL, NULL, 0UL);
   LTC_SET_ASN1(top_seq, 0, LTC_ASN1_INTEGER, zero, 1UL);
   LTC_SET_ASN1(top_seq, 1, LTC_ASN1_SEQUENCE, alg_seq, 2UL);
   LTC_SET_ASN1(top_seq, 2, LTC_ASN1_OCTET_STRING, buf1, buf1len);  
   err=der_decode_sequence(decrypted, decryptedlen, top_seq, 3UL);
   if (err != CRYPT_OK) { goto LBL_ERR; }
   
   /* check alg oid */
   if ((alg_seq[0].size != rsaoid.OIDlen) ||
        XMEMCMP(rsaoid.OID, alg_seq[0].data, rsaoid.OIDlen * sizeof(rsaoid.OID[0]))) {
        err = CRYPT_PK_INVALID_TYPE;
        goto LBL_ERR;
   } 

   err = der_decode_sequence_multi(buf1, top_seq[2].size,
                                   LTC_ASN1_INTEGER, 1UL, zero,
                                   LTC_ASN1_INTEGER, 1UL, key->N,
                                   LTC_ASN1_INTEGER, 1UL, key->e,
                                   LTC_ASN1_INTEGER, 1UL, key->d,
                                   LTC_ASN1_INTEGER, 1UL, key->p,
                                   LTC_ASN1_INTEGER, 1UL, key->q,
                                   LTC_ASN1_INTEGER, 1UL, key->dP,
                                   LTC_ASN1_INTEGER, 1UL, key->dQ,
                                   LTC_ASN1_INTEGER, 1UL, key->qP,
                                   LTC_ASN1_EOL,     0UL, NULL);
   if (err != CRYPT_OK) { goto LBL_ERR; }
   mp_clear_multi(zero, iter, NULL);
   key->type = PK_PRIVATE;
   err = CRYPT_OK;
   goto LBL_FREE;

LBL_ERR:
   mp_clear_multi(key->d, key->e, key->N, key->dQ, key->dP, key->qP, key->p, key->q, zero, iter, NULL);

LBL_FREE:
   if (buf2 != NULL) XFREE(buf2);
   if (buf1 != NULL) XFREE(buf1);

   return err;
}

#endif /* LTC_MRSA */
