diff --git a/agent/Makefile.am b/agent/Makefile.am index ce29462b2..87c9fa122 100644 --- a/agent/Makefile.am +++ b/agent/Makefile.am @@ -45,6 +45,7 @@ gpg_agent_SOURCES = \ cache.c \ trans.c \ findkey.c \ + sexp-secret.c \ pksign.c \ pkdecrypt.c \ genkey.c \ @@ -75,6 +76,7 @@ gpg_agent_DEPENDENCIES = $(resource_objs) gpg_protect_tool_SOURCES = \ protect-tool.c \ + sexp-secret.c \ protect.c cvt-openpgp.c gpg_protect_tool_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) \ diff --git a/agent/agent.h b/agent/agent.h index eb819a0ff..c713944d8 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -639,4 +639,10 @@ extract_private_key (gcry_sexp_t s_key, int req_private_key_data, gcry_mpi_t *mpi_array, int arraysize, gcry_sexp_t *r_curve, gcry_sexp_t *r_flags); +/*-- sexp-secret.c --*/ +size_t fixup_when_ecc_private_key (unsigned char *buf, size_t buflen); + +gpg_error_t sexp_sscan_private_key (gcry_sexp_t *result, size_t *r_erroff, + unsigned char *buf); + #endif /*AGENT_H*/ diff --git a/agent/findkey.c b/agent/findkey.c index 11746f571..be5e1ea95 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -1117,10 +1117,10 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, return err; } - buflen = gcry_sexp_canon_len (buf, 0, NULL, NULL); - err = gcry_sexp_sscan (&s_skey, &erroff, (char*)buf, buflen); - wipememory (buf, buflen); + err = sexp_sscan_private_key (result, &erroff, buf); xfree (buf); + nvc_release (keymeta); + xfree (desc_text_buffer); if (err) { log_error ("failed to build S-Exp (off=%u): %s\n", @@ -1130,15 +1130,9 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, xfree (*r_passphrase); *r_passphrase = NULL; } - nvc_release (keymeta); - xfree (desc_text_buffer); - return err; } - *result = s_skey; - nvc_release (keymeta); - xfree (desc_text_buffer); - return 0; + return err; } diff --git a/agent/protect-tool.c b/agent/protect-tool.c index bcbe4588d..178a20849 100644 --- a/agent/protect-tool.c +++ b/agent/protect-tool.c @@ -404,6 +404,7 @@ read_and_unprotect (ctrl_t ctrl, const char *fname) log_info ("key protection done at [unknown]\n"); } + resultlen = fixup_when_ecc_private_key (result, resultlen); if (opt_armor) { char *p = make_advanced (result, resultlen); diff --git a/agent/sexp-secret.c b/agent/sexp-secret.c new file mode 100644 index 000000000..52920792f --- /dev/null +++ b/agent/sexp-secret.c @@ -0,0 +1,115 @@ +/* sexp-secret.c - SEXP handling of the secret key + * Copyright (C) 2020 g10 Code GmbH. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include "agent.h" +#include "../common/sexp-parse.h" + +/* + * Fixup private key part in the cannonical SEXP. + */ +size_t +fixup_when_ecc_private_key (unsigned char *buf, size_t buflen) +{ + const unsigned char *s; + size_t n; + + s = buf; + if (*s != '(') + return gpg_error (GPG_ERR_INV_SEXP); + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (!smatch (&s, n, "private-key")) + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + if (*s != '(') + return gpg_error (GPG_ERR_UNKNOWN_SEXP); + s++; + if (!smatch (&s, n, "ecc")) + return buflen; + + /* It's ECC */ + while (*s == '(') + { + s++; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + if (n == 1 && *s == 'd') + { + const unsigned char *s0; + size_t n0 = n; + + s += n; + s0 = s; + n = snext (&s); + + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + else if ((n & 1) && !*s) + /* Detect wrongly added 0x00. */ + /* For all existing curves in libgcrypt-1.9 (so far), the + size of private part should be even. */ + { + size_t numsize; + + n--; + buflen--; + numsize = snprintf (s0, s-s0, "%u:", (unsigned int)n); + memmove (s0+numsize, s+1, buflen - (s - buf) - 1); + buflen -= (n0 - numsize); + s = s0+numsize+n; + } + else + s += n; + } + else + { + s += n; + n = snext (&s); + if (!n) + return gpg_error (GPG_ERR_INV_SEXP); + s += n; + } + if ( *s != ')' ) + return gpg_error (GPG_ERR_INV_SEXP); + s++; + } + if (*s != ')') + return gpg_error (GPG_ERR_INV_SEXP); + s++; + + return buflen; +} + +gpg_error_t +sexp_sscan_private_key (gcry_sexp_t *result, size_t *r_erroff, + unsigned char *buf) +{ + gpg_error_t err; + size_t buflen, buflen1; + + buflen = gcry_sexp_canon_len (buf, 0, NULL, NULL); + buflen1 = fixup_when_ecc_private_key (buf, buflen); + err = gcry_sexp_sscan (result, r_erroff, (char*)buf, buflen1); + wipememory (buf, buflen); + + return err; +}