diff --git a/agent/agent.h b/agent/agent.h index eb819a0ff..90d8f5c73 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -245,9 +245,12 @@ struct server_control_s /* Information on the currently used digest (for signing commands). */ struct { + char *data; /* NULL or malloced data of length VALUELEN. If + this is set The other fields are ignored. Used + for PureEdDSA. */ + int valuelen; int algo; unsigned char value[MAX_DIGEST_LEN]; - int valuelen; int raw_value: 1; } digest; unsigned char keygrip[20]; diff --git a/agent/call-scd.c b/agent/call-scd.c index 60365c980..67b2f31ae 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -1001,6 +1001,11 @@ agent_card_pksign (ctrl_t ctrl, if (rc) return rc; + /* FIXME: In the mdalgo case (INDATA,INDATALEN) might be long and + * thus we can't convey it on a single Assuan line. */ + if (!mdalgo) + gpg_error (GPG_ERR_NOT_IMPLEMENTED); + if (indatalen*2 + 50 > DIM(line)) return unlock_scd (ctrl, gpg_error (GPG_ERR_GENERAL)); diff --git a/agent/command-ssh.c b/agent/command-ssh.c index 51f37e18b..881f9372a 100644 --- a/agent/command-ssh.c +++ b/agent/command-ssh.c @@ -2809,6 +2809,8 @@ ssh_handler_sign_request (ctrl_t ctrl, estream_t request, estream_t response) if (!hash_algo) hash_algo = GCRY_MD_SHA1; /* Use the default. */ ctrl->digest.algo = hash_algo; + xfree (ctrl->digest.data); + ctrl->digest.data = NULL; if ((spec.flags & SPEC_FLAG_USE_PKCS1V2)) ctrl->digest.raw_value = 0; else diff --git a/agent/command.c b/agent/command.c index 6f179cb08..d374fd06f 100644 --- a/agent/command.c +++ b/agent/command.c @@ -243,6 +243,8 @@ reset_notify (assuan_context_t ctx, char *line) memset (ctrl->keygrip, 0, 20); ctrl->have_keygrip = 0; ctrl->digest.valuelen = 0; + xfree (ctrl->digest.data); + ctrl->digest.data = NULL; xfree (ctrl->server_local->keydesc); ctrl->server_local->keydesc = NULL; @@ -704,20 +706,23 @@ cmd_setkeydesc (assuan_context_t ctx, char *line) static const char hlp_sethash[] = - "SETHASH (--hash=)|() \n" + "SETHASH (--hash=)|() ]\n" + "SETHASH --inquire\n" "\n" "The client can use this command to tell the server about the data\n" - "(which usually is a hash) to be signed."; + "(which usually is a hash) to be signed. The option --inquire is\n" + "used to ask back for to-be-signed data in case of PureEdDSA"; static gpg_error_t cmd_sethash (assuan_context_t ctx, char *line) { - int rc; + gpg_error_t err; size_t n; char *p; ctrl_t ctrl = assuan_get_pointer (ctx); unsigned char *buf; char *endp; int algo; + int opt_inquire; /* Parse the alternative hash options which may be used instead of the algo number. */ @@ -740,47 +745,89 @@ cmd_sethash (assuan_context_t ctx, char *line) else if (has_option (line, "--hash=tls-md5sha1")) algo = MD_USER_TLS_MD5SHA1; else - return set_error (GPG_ERR_ASS_PARAMETER, "invalid hash algorithm"); + { + err = set_error (GPG_ERR_ASS_PARAMETER, "invalid hash algorithm"); + goto leave; + } } else algo = 0; + opt_inquire = has_option (line, "--inquire"); line = skip_options (line); - if (!algo) + if (!algo && !opt_inquire) { /* No hash option has been given: require an algo number instead */ algo = (int)strtoul (line, &endp, 10); for (line = endp; *line == ' ' || *line == '\t'; line++) ; if (!algo || gcry_md_test_algo (algo)) - return set_error (GPG_ERR_UNSUPPORTED_ALGORITHM, NULL); + { + err = set_error (GPG_ERR_UNSUPPORTED_ALGORITHM, NULL); + goto leave; + } } + xfree (ctrl->digest.data); + ctrl->digest.data = NULL; ctrl->digest.algo = algo; ctrl->digest.raw_value = 0; - /* Parse the hash value. */ - n = 0; - rc = parse_hexstring (ctx, line, &n); - if (rc) - return rc; - n /= 2; - if (algo == MD_USER_TLS_MD5SHA1 && n == 36) - ; - else if (n != 16 && n != 20 && n != 24 - && n != 28 && n != 32 && n != 48 && n != 64) - return set_error (GPG_ERR_ASS_PARAMETER, "unsupported length of hash"); + if (opt_inquire) + { + /* We limit the to-be-signed data to some reasonable size which + * may eventually allow us to pass that even to smartcards. */ + size_t maxlen = 2048; - if (n > MAX_DIGEST_LEN) - return set_error (GPG_ERR_ASS_PARAMETER, "hash value to long"); + if (algo) + { + err = set_error (GPG_ERR_ASS_PARAMETER, + "both --inquire and an algo are specified"); + goto leave; + } - buf = ctrl->digest.value; - ctrl->digest.valuelen = n; - for (p=line, n=0; n < ctrl->digest.valuelen; p += 2, n++) - buf[n] = xtoi_2 (p); - for (; n < ctrl->digest.valuelen; n++) - buf[n] = 0; - return 0; + err = print_assuan_status (ctx, "INQUIRE_MAXLEN", "%zu", maxlen); + if (!err) + err = assuan_inquire (ctx, "TBSDATA", &buf, &n, maxlen); + if (err) + goto leave; + + ctrl->digest.data = buf; + ctrl->digest.valuelen = n; + } + else + { + /* Parse the hash value. */ + n = 0; + err = parse_hexstring (ctx, line, &n); + if (err) + goto leave; + n /= 2; + if (algo == MD_USER_TLS_MD5SHA1 && n == 36) + ; + else if (n != 16 && n != 20 && n != 24 + && n != 28 && n != 32 && n != 48 && n != 64) + { + err = set_error (GPG_ERR_ASS_PARAMETER, "unsupported length of hash"); + goto leave; + } + + if (n > MAX_DIGEST_LEN) + { + err = set_error (GPG_ERR_ASS_PARAMETER, "hash value to long"); + goto leave; + } + + buf = ctrl->digest.value; + ctrl->digest.valuelen = n; + for (p=line, n=0; n < ctrl->digest.valuelen; p += 2, n++) + buf[n] = xtoi_2 (p); + for (; n < ctrl->digest.valuelen; n++) + buf[n] = 0; + } + + leave: + return leave_cmd (ctx, err); } @@ -3627,6 +3674,7 @@ start_command_handler (ctrl_t ctrl, gnupg_fd_t listen_fd, gnupg_fd_t fd) ctrl->server_local->assuan_ctx = ctx; ctrl->server_local->use_cache_for_signing = 1; + ctrl->digest.data = NULL; ctrl->digest.raw_value = 0; assuan_set_io_monitor (ctx, io_monitor, NULL); diff --git a/agent/divert-scd.c b/agent/divert-scd.c index d8076d158..7587d4478 100644 --- a/agent/divert-scd.c +++ b/agent/divert-scd.c @@ -437,7 +437,14 @@ divert_pksign (ctrl_t ctrl, const char *desc_text, const unsigned char *grip, /* Note that the KID may be an keyref or a keygrip. The signing * functions handle both. */ - if (algo == MD_USER_TLS_MD5SHA1) + if (!algo) + { + /* This is the PureEdDSA case. (DIGEST,DIGESTLEN) this the + * entire data which will be signed. */ + rc = agent_card_pksign (ctrl, kid, getpin_cb, ctrl, NULL, + 0, digest, digestlen, &sigval, &siglen); + } + else if (algo == MD_USER_TLS_MD5SHA1) { int save = ctrl->use_auth_call; ctrl->use_auth_call = 1; diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index a7573c2ab..4466389a3 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -1937,6 +1937,8 @@ agent_deinit_default_ctrl (ctrl_t ctrl) unregister_progress_cb (); session_env_release (ctrl->session_env); + xfree (ctrl->digest.data); + ctrl->digest.data = NULL; if (ctrl->lc_ctype) xfree (ctrl->lc_ctype); if (ctrl->lc_messages) diff --git a/agent/pksign.c b/agent/pksign.c index 3474f9434..76b0c3f85 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -304,6 +304,11 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce, data = overridedata; datalen = overridedatalen; } + else if (ctrl->digest.data) + { + data = ctrl->digest.data; + datalen = ctrl->digest.valuelen; + } else { data = ctrl->digest.value;