build_privkey_template (app_t app, int keyno,
                        const unsigned char *rsa_n, size_t rsa_n_len,
                        const unsigned char *rsa_e, size_t rsa_e_len,
                        const unsigned char *rsa_p, size_t rsa_p_len,
                        const unsigned char *rsa_q, size_t rsa_q_len,
                        const unsigned char *rsa_u, size_t rsa_u_len,
                        const unsigned char *rsa_dp, size_t rsa_dp_len,
                        const unsigned char *rsa_dq, size_t rsa_dq_len,
                        unsigned char **result, size_t *resultlen)
{
  size_t rsa_e_reqlen;
  unsigned char privkey[7*(1+3+3)];
  size_t privkey_len;
  unsigned char exthdr[2+2+3];
  size_t exthdr_len;
  unsigned char suffix[2+3];
  size_t suffix_len;
  unsigned char *tp;
  size_t datalen;
  unsigned char *template;
  size_t template_size;

  *result = NULL;
  *resultlen = 0;

  switch (app->app_local->keyattr[keyno].rsa.format)
    {
    case RSA_STD:
    case RSA_STD_N:
    case RSA_CRT:
    case RSA_CRT_N:
      break;

    default:
      return gpg_error (GPG_ERR_INV_VALUE);
    }

  /* Get the required length for E. Rounded up to the nearest byte  */
  rsa_e_reqlen = (app->app_local->keyattr[keyno].rsa.e_bits + 7) / 8;
  assert (rsa_e_len <= rsa_e_reqlen);

  /* Build the 7f48 cardholder private key template.  */
  datalen = 0;
  tp = privkey;

  tp += add_tlv (tp, 0x91, rsa_e_reqlen);
  datalen += rsa_e_reqlen;

  tp += add_tlv (tp, 0x92, rsa_p_len);
  datalen += rsa_p_len;

  tp += add_tlv (tp, 0x93, rsa_q_len);
  datalen += rsa_q_len;

  if (app->app_local->keyattr[keyno].rsa.format == RSA_CRT
      || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
    {
      tp += add_tlv (tp, 0x94, rsa_u_len);
      datalen += rsa_u_len;
      tp += add_tlv (tp, 0x95, rsa_dp_len);
      datalen += rsa_dp_len;
      tp += add_tlv (tp, 0x96, rsa_dq_len);
      datalen += rsa_dq_len;
    }

  if (app->app_local->keyattr[keyno].rsa.format == RSA_STD_N
      || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
    {
      tp += add_tlv (tp, 0x97, rsa_n_len);
      datalen += rsa_n_len;
    }
  privkey_len = tp - privkey;

  /* Build the extended header list without the private key template.  */
  tp = exthdr;
  *tp++ = keyno ==0 ? 0xb6 : keyno == 1? 0xb8 : 0xa4;
  *tp++ = 0;
  tp += add_tlv (tp, 0x7f48, privkey_len);
  exthdr_len = tp - exthdr;

  /* Build the 5f48 suffix of the data.  */
  tp = suffix;
  tp += add_tlv (tp, 0x5f48, datalen);
  suffix_len = tp - suffix;

  /* Now concatenate everything.  */
  template_size = (1 + 3   /* 0x4d and len. */
                   + exthdr_len
                   + privkey_len
                   + suffix_len
                   + datalen);
  tp = template = xtrymalloc_secure (template_size);
  if (!template)
    return gpg_error_from_syserror ();

  tp += add_tlv (tp, 0x4d, exthdr_len + privkey_len + suffix_len + datalen);
  memcpy (tp, exthdr, exthdr_len);
  tp += exthdr_len;
  memcpy (tp, privkey, privkey_len);
  tp += privkey_len;
  memcpy (tp, suffix, suffix_len);
  tp += suffix_len;

  memcpy (tp, rsa_e, rsa_e_len);
  if (rsa_e_len < rsa_e_reqlen)
    {
      /* Right justify E. */
      memmove (tp + rsa_e_reqlen - rsa_e_len, tp, rsa_e_len);
      memset (tp, 0, rsa_e_reqlen - rsa_e_len);
    }
  tp += rsa_e_reqlen;

  memcpy (tp, rsa_p, rsa_p_len);
  tp += rsa_p_len;

  memcpy (tp, rsa_q, rsa_q_len);
  tp += rsa_q_len;

  if (app->app_local->keyattr[keyno].rsa.format == RSA_CRT
      || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
    {
      memcpy (tp, rsa_u, rsa_u_len);
      tp += rsa_u_len;
      memcpy (tp, rsa_dp, rsa_dp_len);
      tp += rsa_dp_len;
      memcpy (tp, rsa_dq, rsa_dq_len);
      tp += rsa_dq_len;
    }

  if (app->app_local->keyattr[keyno].rsa.format == RSA_STD_N
      || app->app_local->keyattr[keyno].rsa.format == RSA_CRT_N)
    {
      memcpy (tp, rsa_n, rsa_n_len);
      tp += rsa_n_len;
    }

  /* Sanity check.  We don't know the exact length because we
     allocated 3 bytes for the first length header.  */
  assert (tp - template <= template_size);

  *result = template;
  *resultlen = tp - template;
  return 0;
}