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:
parent
c6d1f2f08c
commit
12af2630cf
14 changed files with 1831 additions and 13 deletions
161
agent/findkey.c
161
agent/findkey.c
|
@ -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 ();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue