1
0
Fork 0
mirror of git://git.gnupg.org/gnupg.git synced 2025-07-03 22:56:33 +02:00

common: Add support for the new extended private key format.

* agent/findkey.c (write_extended_private_key): New function.
(agent_write_private_key): Detect if an existing file is in extended
format and update the key within if it is.
(read_key_file): Handle the new format.
* agent/keyformat.txt: Document the new format.
* common/Makefile.am: Add the new files.
* common/private-keys.c: New file.
* common/private-keys.h: Likewise.
* common/t-private-keys.c: Likewise.
* common/util.h (alphap, alnump): New macros.
* tests/migrations: Add test demonstrating that we can cope with the
new format.

--
GnuPG 2.3+ will use a new format to store private keys that is both
more flexible and easier to read and edit by human beings.  The new
format stores name,value-pairs using the common mail and http header
convention.

This patch adds the parser and support code and prepares GnuPG 2.1 for
the new format.

Signed-off-by: Justus Winter <justus@g10code.com>
This commit is contained in:
Justus Winter 2016-04-08 19:21:12 +02:00
parent c6d1f2f08c
commit 12af2630cf
14 changed files with 1831 additions and 13 deletions

View file

@ -35,6 +35,7 @@
#include "agent.h"
#include "i18n.h"
#include "../common/ssh-utils.h"
#include "../common/private-keys.h"
#ifndef O_BINARY
#define O_BINARY 0
@ -51,6 +52,75 @@ struct try_unprotect_arg_s
};
static gpg_error_t
write_extended_private_key (char *fname, estream_t fp,
const void *buf, size_t len)
{
gpg_error_t err;
pkc_t pk = NULL;
gcry_sexp_t key = NULL;
int remove = 0;
int line;
err = pkc_parse (&pk, &line, fp);
if (err)
{
log_error ("error parsing '%s' line %d: %s\n",
fname, line, gpg_strerror (err));
goto leave;
}
err = gcry_sexp_sscan (&key, NULL, buf, len);
if (err)
goto leave;
err = pkc_set_private_key (pk, key);
if (err)
goto leave;
err = es_fseek (fp, 0, SEEK_SET);
if (err)
goto leave;
err = pkc_write (pk, fp);
if (err)
{
log_error ("error writing '%s': %s\n", fname, gpg_strerror (err));
remove = 1;
goto leave;
}
if (ftruncate (es_fileno (fp), es_ftello (fp)))
{
err = gpg_error_from_syserror ();
log_error ("error truncating '%s': %s\n", fname, gpg_strerror (err));
remove = 1;
goto leave;
}
if (es_fclose (fp))
{
err = gpg_error_from_syserror ();
log_error ("error closing '%s': %s\n", fname, gpg_strerror (err));
remove = 1;
goto leave;
}
else
fp = NULL;
bump_key_eventcounter ();
leave:
if (fp)
es_fclose (fp);
if (remove)
gnupg_remove (fname);
xfree (fname);
gcry_sexp_release (key);
pkc_release (pk);
return err;
}
/* Write an S-expression formatted key to our key storage. With FORCE
passed as true an existing key with the given GRIP will get
overwritten. */
@ -77,7 +147,7 @@ agent_write_private_key (const unsigned char *grip,
return gpg_error (GPG_ERR_EEXIST);
}
fp = es_fopen (fname, force? "wb,mode=-rw" : "wbx,mode=-rw");
fp = es_fopen (fname, force? "rb+,mode=-rw" : "wbx,mode=-rw");
if (!fp)
{
gpg_error_t tmperr = gpg_error_from_syserror ();
@ -86,6 +156,38 @@ agent_write_private_key (const unsigned char *grip,
return tmperr;
}
/* See if an existing key is in extended format. */
if (force)
{
gpg_error_t rc;
char first;
if (es_fread (&first, 1, 1, fp) != 1)
{
rc = gpg_error_from_syserror ();
log_error ("error reading first byte from '%s': %s\n",
fname, strerror (errno));
xfree (fname);
es_fclose (fp);
return rc;
}
rc = es_fseek (fp, 0, SEEK_SET);
if (rc)
{
log_error ("error seeking in '%s': %s\n", fname, strerror (errno));
xfree (fname);
es_fclose (fp);
return rc;
}
if (first != '(')
{
/* Key is in extended format. */
return write_extended_private_key (fname, fp, buffer, length);
}
}
if (es_fwrite (buffer, length, 1, fp) != 1)
{
gpg_error_t tmperr = gpg_error_from_syserror ();
@ -95,6 +197,18 @@ agent_write_private_key (const unsigned char *grip,
xfree (fname);
return tmperr;
}
/* When force is given, the file might have to be truncated. */
if (force && ftruncate (es_fileno (fp), es_ftello (fp)))
{
gpg_error_t tmperr = gpg_error_from_syserror ();
log_error ("error truncating '%s': %s\n", fname, gpg_strerror (tmperr));
es_fclose (fp);
gnupg_remove (fname);
xfree (fname);
return tmperr;
}
if (es_fclose (fp))
{
gpg_error_t tmperr = gpg_error_from_syserror ();
@ -531,6 +645,7 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result)
size_t buflen, erroff;
gcry_sexp_t s_skey;
char hexgrip[40+4+1];
char first;
*result = NULL;
@ -548,6 +663,50 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result)
return rc;
}
if (es_fread (&first, 1, 1, fp) != 1)
{
rc = gpg_error_from_syserror ();
log_error ("error reading first byte from '%s': %s\n",
fname, strerror (errno));
xfree (fname);
es_fclose (fp);
return rc;
}
rc = es_fseek (fp, 0, SEEK_SET);
if (rc)
{
log_error ("error seeking in '%s': %s\n", fname, strerror (errno));
xfree (fname);
es_fclose (fp);
return rc;
}
if (first != '(')
{
/* Key is in extended format. */
pkc_t pk;
int line;
rc = pkc_parse (&pk, &line, fp);
es_fclose (fp);
if (rc)
log_error ("error parsing '%s' line %d: %s\n",
fname, line, gpg_strerror (rc));
else
{
rc = pkc_get_private_key (pk, result);
pkc_release (pk);
if (rc)
log_error ("error getting private key from '%s': %s\n",
fname, gpg_strerror (rc));
}
xfree (fname);
return rc;
}
if (fstat (es_fileno (fp), &st))
{
rc = gpg_error_from_syserror ();