ecc_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
              void *pincb_arg, int keyno,
              const unsigned char *buf, size_t buflen, int depth)
{
  gpg_error_t err;
  const unsigned char *tok;
  size_t toklen;
  int last_depth1, last_depth2;
  const unsigned char *ecc_q = NULL;
  const unsigned char *ecc_d = NULL;
  size_t ecc_q_len, ecc_d_len;
  unsigned char *template = NULL;
  size_t template_len;
  unsigned char fprbuf[20];
  u32 created_at = 0;
  int curve = CURVE_UNKNOWN;

  /* (private-key(ecc(curve%s)(q%m)(d%m))(created-at%d)):
     curve = "NIST P-256" */
  /* (private-key(ecc(curve%s)(q%m)(d%m))(created-at%d)):
     curve = "secp256k1" */
  /* (private-key(ecc(curve%s)(flags eddsa)(q%m)(d%m))(created-at%d)):
      curve = "Ed25519" */
  last_depth1 = depth;
  while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
         && depth && depth >= last_depth1)
    {
      if (tok)
        {
          err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
          goto leave;
        }
      if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
        goto leave;

      if (tok && toklen == 5 && !memcmp (tok, "curve", 5))
        {
          if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
            goto leave;

          if (tok && toklen == 10 && !memcmp (tok, "NIST P-256", 10))
            curve = CURVE_NIST_P256;
          else if (tok && toklen == 9 && !memcmp (tok, "secp256k1", 9))
            curve = CURVE_SEC_P256K1;
          else if (tok && toklen == 7 && !memcmp (tok, "Ed25519", 7))
            curve = CURVE_ED25519;
          else
            {
              log_error (_("unsupported curve\n"));
              err = gpg_error (GPG_ERR_INV_VALUE);
              goto leave;
            }
        }
      else if (tok && toklen == 1)
        {
          const unsigned char **buf2;
          size_t *buf2len;

          switch (*tok)
            {
            case 'q': buf2 = &ecc_q; buf2len = &ecc_q_len; break;
            case 'd': buf2 = &ecc_d; buf2len = &ecc_d_len; break;
            default: buf2 = NULL;  buf2len = NULL; break;
            }
          if (buf2 && *buf2)
            {
              err = gpg_error (GPG_ERR_DUP_VALUE);
              goto leave;
            }
          if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
            goto leave;
          if (tok && buf2 && curve != CURVE_ED25519)
            /* It's MPI.  Strip off leading zero bytes and save. */
            for (;toklen && !*tok; toklen--, tok++)
              ;

          *buf2 = tok;
          *buf2len = toklen;
        }
      /* Skip until end of list. */
      last_depth2 = depth;
      while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
             && depth && depth >= last_depth2)
        ;
      if (err)
        goto leave;
    }
  /* Parse other attributes. */
  last_depth1 = depth;
  while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
         && depth && depth >= last_depth1)
    {
      if (tok)
        {
          err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
          goto leave;
        }
      if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
        goto leave;
      if (tok && toklen == 10 && !memcmp ("created-at", tok, toklen))
        {
          if ((err = parse_sexp (&buf,&buflen,&depth,&tok,&toklen)))
            goto leave;
          if (tok)
            {
              for (created_at=0; toklen && *tok && *tok >= '0' && *tok <= '9';
                   tok++, toklen--)
                created_at = created_at*10 + (*tok - '0');
            }
        }
      /* Skip until end of list. */
      last_depth2 = depth;
      while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
             && depth && depth >= last_depth2)
        ;
      if (err)
        goto leave;
    }


  /* Check that we have all parameters and that they match the card
     description. */
  if (!created_at)
    {
      log_error (_("creation timestamp missing\n"));
      err = gpg_error (GPG_ERR_INV_VALUE);
      goto leave;
    }

  if (opt.verbose)
    log_info ("ECC private key size is %u bytes\n", (unsigned int)ecc_d_len);

  /* We need to remove the cached public key.  */
  xfree (app->app_local->pk[keyno].key);
  app->app_local->pk[keyno].key = NULL;
  app->app_local->pk[keyno].keylen = 0;
  app->app_local->pk[keyno].read_done = 0;

  if (app->app_local->extcap.is_v2)
    {
      /* Build the private key template as described in section 4.3.3.7 of
         the OpenPGP card specs version 2.0.  */
      int exmode;

      err = build_ecc_privkey_template (app, keyno,
                                        ecc_d, ecc_d_len,
                                        &template, &template_len);
      if (err)
        goto leave;

      /* Prepare for storing the key.  */
      err = verify_chv3 (app, pincb, pincb_arg);
      if (err)
        goto leave;

      /* Store the key. */
      if (app->app_local->cardcap.ext_lc_le && template_len > 254)
        exmode = 1;    /* Use extended length w/o a limit.  */
      else if (app->app_local->cardcap.cmd_chaining && template_len > 254)
        exmode = -254;
      else
        exmode = 0;
      err = iso7816_put_data_odd (app->slot, exmode, 0x3fff,
                                  template, template_len);
    }
  else
    return gpg_error (GPG_ERR_NOT_SUPPORTED);

  if (err)
    {
      log_error (_("failed to store the key: %s\n"), gpg_strerror (err));
      goto leave;
    }

  err = store_fpr (app, keyno, created_at, fprbuf,
                   curve == CURVE_ED25519 ? KEY_TYPE_EDDSA : KEY_TYPE_ECC,
                   curve == CURVE_ED25519 ?
                   "\x09\x2b\x06\x01\x04\x01\xda\x47\x0f\x01"
                   : curve == CURVE_NIST_P256 ?
                   "\x08\x2a\x86\x48\xce\x3d\x03\x01\x07"
                   : "\x05\x2b\x81\x04\x00\x0a",
                   (size_t)(curve == CURVE_ED25519 ? 10
                            : curve == CURVE_NIST_P256? 9 : 6),
                   ecc_q, ecc_q_len, "\x03\x01\x08\x07", (size_t)4);
  if (err)
    goto leave;


 leave:
  xfree (template);
  return err;
}