mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01: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
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 ();
|
||||
|
@ -16,7 +16,71 @@ and should have permissions 700.
|
||||
The secret keys are stored in files with a name matching the
|
||||
hexadecimal representation of the keygrip[2] and suffixed with ".key".
|
||||
|
||||
* Unprotected Private Key Format
|
||||
* Extended Private Key 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. Example (here indented with two spaces):
|
||||
|
||||
Description: Key to sign all GnuPG released tarballs.
|
||||
The key is actually stored on a smart card.
|
||||
Use-for-ssh: yes
|
||||
OpenSSH-cert: long base64 encoded string wrapped so that this
|
||||
key file can be easily edited with a standard editor.
|
||||
Key: (shadowed-private-key
|
||||
(rsa
|
||||
(n #00AA1AD2A55FD8C8FDE9E1941772D9CC903FA43B268CB1B5A1BAFDC900
|
||||
2961D8AEA153424DC851EF13B83AC64FBE365C59DC1BD3E83017C90D4365B4
|
||||
83E02859FC13DB5842A00E969480DB96CE6F7D1C03600392B8E08EF0C01FC7
|
||||
19F9F9086B25AD39B4F1C2A2DF3E2BE317110CFFF21D4A11455508FE407997
|
||||
601260816C8422297C0637BB291C3A079B9CB38A92CE9E551F80AA0EBF4F0E
|
||||
72C3F250461E4D31F23A7087857FC8438324A013634563D34EFDDCBF2EA80D
|
||||
F9662C9CCD4BEF2522D8BDFED24CEF78DC6B309317407EAC576D889F88ADA0
|
||||
8C4FFB480981FB68C5C6CA27503381D41018E6CDC52AAAE46B166BDC10637A
|
||||
E186A02BA2497FDC5D1221#)
|
||||
(e #00010001#)
|
||||
(shadowed t1-v1
|
||||
(#D2760001240102000005000011730000# OPENPGP.1)
|
||||
)))
|
||||
|
||||
GnuPG 2.2 is able to read and update keys using the new format, but
|
||||
will not create new files using the new format. Furthermore, it only
|
||||
makes use of the value stored under the name 'Key:'.
|
||||
|
||||
Keys in the extended format can be recognized by looking at the first
|
||||
byte of the file. If it starts with a '(' it is a naked S-expression,
|
||||
otherwise it is a key in extended format.
|
||||
|
||||
** Names
|
||||
|
||||
A name must start with a letter and end with a colon. Valid
|
||||
characters are all ASCII letters, numbers and the hyphen. Comparison
|
||||
of names is done case insensitively. Names may be used several times
|
||||
to represent an array of values.
|
||||
|
||||
The name "Key:" is special in that it may occur only once and the
|
||||
associated value holds the actual S-expression with the cryptographic
|
||||
key. The S-expression is formatted using the 'Advanced Format'
|
||||
(GCRYSEXP_FMT_ADVANCED) that avoids non-printable characters so that
|
||||
the file can be easily inspected and edited. See section 'Private Key
|
||||
Format' below for details.
|
||||
|
||||
** Values
|
||||
|
||||
Values are UTF-8 encoded strings. Values can be wrapped at any point,
|
||||
and continued in the next line indicated by leading whitespace. A
|
||||
continuation line with one leading space does not introduce a blank so
|
||||
that the lines can be effectively concatenated. A blank line as part
|
||||
of a continuation line encodes a newline.
|
||||
|
||||
** Comments
|
||||
|
||||
Lines containing only whitespace, and lines starting with whitespace
|
||||
followed by '#' are considered to be comments and are ignored.
|
||||
|
||||
* Private Key Format
|
||||
** Unprotected Private Key Format
|
||||
|
||||
The content of the file is an S-Expression like the ones used with
|
||||
Libgcrypt. Here is an example of an unprotected file:
|
||||
@ -42,7 +106,7 @@ optional but required for some operations to calculate the fingerprint
|
||||
of the key. This timestamp should be a string with the number of
|
||||
seconds since Epoch or an ISO time string (yyyymmddThhmmss).
|
||||
|
||||
* Protected Private Key Format
|
||||
** Protected Private Key Format
|
||||
|
||||
A protected key is like this:
|
||||
|
||||
@ -67,7 +131,7 @@ optional; the isotimestamp is 15 bytes long (e.g. "19610711T172000").
|
||||
|
||||
The currently defined protection modes are:
|
||||
|
||||
** openpgp-s2k3-sha1-aes-cbc
|
||||
*** openpgp-s2k3-sha1-aes-cbc
|
||||
|
||||
This describes an algorithm using using AES in CBC mode for
|
||||
encryption, SHA-1 for integrity protection and the String to Key
|
||||
@ -116,7 +180,7 @@ The currently defined protection modes are:
|
||||
the stored one - If they don't match the integrity of the key is not
|
||||
given.
|
||||
|
||||
** openpgp-s2k3-ocb-aes
|
||||
*** openpgp-s2k3-ocb-aes
|
||||
|
||||
This describes an algorithm using using AES-128 in OCB mode, a nonce
|
||||
of 96 bit, a taglen of 128 bit, and the String to Key algorithm 3
|
||||
@ -154,7 +218,7 @@ The currently defined protection modes are:
|
||||
(protected-at "18950523T000000")
|
||||
)
|
||||
|
||||
** openpgp-native
|
||||
*** openpgp-native
|
||||
|
||||
This is a wrapper around the OpenPGP Private Key Transport format
|
||||
which resembles the standard OpenPGP format and allows the use of an
|
||||
@ -191,7 +255,7 @@ The currently defined protection modes are:
|
||||
(uri http://foo.bar x-foo:whatever_you_want)
|
||||
(comment whatever))
|
||||
|
||||
* Shadowed Private Key Format
|
||||
** Shadowed Private Key Format
|
||||
|
||||
To keep track of keys stored on IC cards we use a third format for
|
||||
private kyes which are called shadow keys as they are only a reference
|
||||
@ -219,7 +283,7 @@ readers don't allow passing a variable length PIN.
|
||||
|
||||
More items may be added to the list.
|
||||
|
||||
* OpenPGP Private Key Transfer Format
|
||||
** OpenPGP Private Key Transfer Format
|
||||
|
||||
This format is used to transfer keys between gpg and gpg-agent.
|
||||
|
||||
@ -251,7 +315,7 @@ This format is used to transfer keys between gpg and gpg-agent.
|
||||
* S2KSALT is the 8 byte salt
|
||||
* S2KCOUNT is the count value from RFC-4880.
|
||||
|
||||
* Persistent Passphrase Format
|
||||
** Persistent Passphrase Format
|
||||
|
||||
Note: That this has not yet been implemented.
|
||||
|
||||
|
@ -89,7 +89,8 @@ common_sources = \
|
||||
strlist.c strlist.h \
|
||||
call-gpg.c call-gpg.h \
|
||||
exectool.c exectool.h \
|
||||
server-help.c server-help.h
|
||||
server-help.c server-help.h \
|
||||
private-keys.c private-keys.h
|
||||
|
||||
if HAVE_W32_SYSTEM
|
||||
common_sources += w32-reg.c w32-afunix.c w32-afunix.h
|
||||
@ -154,7 +155,8 @@ endif
|
||||
module_tests = t-stringhelp t-timestuff \
|
||||
t-convert t-percent t-gettime t-sysutils t-sexputil \
|
||||
t-session-env t-openpgp-oid t-ssh-utils \
|
||||
t-mapstrings t-zb32 t-mbox-util t-iobuf t-strlist
|
||||
t-mapstrings t-zb32 t-mbox-util t-iobuf t-strlist \
|
||||
t-private-keys
|
||||
if !HAVE_W32CE_SYSTEM
|
||||
module_tests += t-exechelp
|
||||
endif
|
||||
@ -203,6 +205,7 @@ t_zb32_LDADD = $(t_common_ldadd)
|
||||
t_mbox_util_LDADD = $(t_common_ldadd)
|
||||
t_iobuf_LDADD = $(t_common_ldadd)
|
||||
t_strlist_LDADD = $(t_common_ldadd)
|
||||
t_private_keys_LDADD = $(t_common_ldadd)
|
||||
|
||||
# System specific test
|
||||
if HAVE_W32_SYSTEM
|
||||
|
740
common/private-keys.c
Normal file
740
common/private-keys.c
Normal file
@ -0,0 +1,740 @@
|
||||
/* private-keys.c - Parser and writer for the extended private key format.
|
||||
* Copyright (C) 2016 g10 Code GmbH
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of either
|
||||
*
|
||||
* - the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* or
|
||||
*
|
||||
* - the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* or both in parallel, as here.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <assert.h>
|
||||
#include <gcrypt.h>
|
||||
#include <gpg-error.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "private-keys.h"
|
||||
#include "mischelp.h"
|
||||
#include "strlist.h"
|
||||
#include "util.h"
|
||||
|
||||
struct private_key_container
|
||||
{
|
||||
struct private_key_entry *first;
|
||||
struct private_key_entry *last;
|
||||
};
|
||||
|
||||
|
||||
struct private_key_entry
|
||||
{
|
||||
struct private_key_entry *prev;
|
||||
struct private_key_entry *next;
|
||||
|
||||
/* The name. Comments and blank lines have NAME set to NULL. */
|
||||
char *name;
|
||||
|
||||
/* The value as stored in the file. We store it when when we parse
|
||||
a file so that we can reproduce it. */
|
||||
strlist_t raw_value;
|
||||
|
||||
/* The decoded value. */
|
||||
char *value;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* Allocation and deallocation. */
|
||||
|
||||
/* Allocate a private key container structure. */
|
||||
pkc_t
|
||||
pkc_new (void)
|
||||
{
|
||||
return xtrycalloc (1, sizeof (struct private_key_container));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pke_release (pke_t entry)
|
||||
{
|
||||
if (entry == NULL)
|
||||
return;
|
||||
|
||||
xfree (entry->name);
|
||||
if (entry->value)
|
||||
wipememory (entry->value, strlen (entry->value));
|
||||
xfree (entry->value);
|
||||
free_strlist_wipe (entry->raw_value);
|
||||
xfree (entry);
|
||||
}
|
||||
|
||||
|
||||
/* Release a private key container structure. */
|
||||
void
|
||||
pkc_release (pkc_t pk)
|
||||
{
|
||||
pke_t e, next;
|
||||
|
||||
if (pk == NULL)
|
||||
return;
|
||||
|
||||
for (e = pk->first; e; e = next)
|
||||
{
|
||||
next = e->next;
|
||||
pke_release (e);
|
||||
}
|
||||
|
||||
xfree (pk);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Dealing with names and values. */
|
||||
|
||||
/* Check whether the given name is valid. Valid names start with a
|
||||
letter, end with a colon, and contain only alphanumeric characters
|
||||
and the hyphen. */
|
||||
static int
|
||||
valid_name (const char *name)
|
||||
{
|
||||
size_t i, len = strlen (name);
|
||||
|
||||
if (! alphap (name) || len == 0 || name[len - 1] != ':')
|
||||
return 0;
|
||||
|
||||
for (i = 1; i < len - 1; i++)
|
||||
if (! alnump (&name[i]) && name[i] != '-')
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Makes sure that ENTRY has a RAW_VALUE. */
|
||||
static gpg_error_t
|
||||
assert_raw_value (pke_t entry)
|
||||
{
|
||||
gpg_error_t err = 0;
|
||||
size_t len, offset;
|
||||
#define LINELEN 70
|
||||
char buf[LINELEN+3];
|
||||
|
||||
if (entry->raw_value)
|
||||
return 0;
|
||||
|
||||
len = strlen (entry->value);
|
||||
offset = 0;
|
||||
while (len)
|
||||
{
|
||||
size_t amount, linelen = LINELEN;
|
||||
|
||||
/* On the first line we need to subtract space for the name. */
|
||||
if (entry->raw_value == NULL && strlen (entry->name) < linelen)
|
||||
linelen -= strlen (entry->name);
|
||||
|
||||
/* See if the rest of the value fits in this line. */
|
||||
if (len <= linelen)
|
||||
amount = len;
|
||||
else
|
||||
{
|
||||
size_t i;
|
||||
|
||||
/* Find a suitable space to break on. */
|
||||
for (i = linelen - 1; linelen - i < 30 && linelen - i > offset; i--)
|
||||
if (ascii_isspace (entry->value[i]))
|
||||
break;
|
||||
|
||||
if (ascii_isspace (entry->value[i]))
|
||||
{
|
||||
/* Found one. */
|
||||
amount = i;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Just induce a hard break. */
|
||||
amount = linelen;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf (buf, sizeof buf, " %.*s\n", (int) amount,
|
||||
&entry->value[offset]);
|
||||
if (append_to_strlist_try (&entry->raw_value, buf) == NULL)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
offset += amount;
|
||||
len -= amount;
|
||||
}
|
||||
|
||||
leave:
|
||||
if (err)
|
||||
{
|
||||
free_strlist_wipe (entry->raw_value);
|
||||
entry->raw_value = NULL;
|
||||
}
|
||||
|
||||
return err;
|
||||
#undef LINELEN
|
||||
}
|
||||
|
||||
|
||||
/* Computes the length of the value encoded as continuation. If
|
||||
*SWALLOW_WS is set, all whitespace at the beginning of S is
|
||||
swallowed. If START is given, a pointer to the beginning of the
|
||||
value is stored there. */
|
||||
static size_t
|
||||
continuation_length (const char *s, int *swallow_ws, const char **start)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
if (*swallow_ws)
|
||||
{
|
||||
/* The previous line was a blank line and we inserted a newline.
|
||||
Swallow all whitespace at the beginning of this line. */
|
||||
while (ascii_isspace (*s))
|
||||
s++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Iff a continuation starts with more than one space, it
|
||||
encodes a space. */
|
||||
if (ascii_isspace (*s))
|
||||
s++;
|
||||
}
|
||||
|
||||
/* Strip whitespace at the end. */
|
||||
len = strlen (s);
|
||||
while (len > 0 && ascii_isspace (s[len-1]))
|
||||
len--;
|
||||
|
||||
if (len == 0)
|
||||
{
|
||||
/* Blank lines encode newlines. */
|
||||
len = 1;
|
||||
s = "\n";
|
||||
*swallow_ws = 1;
|
||||
}
|
||||
else
|
||||
*swallow_ws = 0;
|
||||
|
||||
if (start)
|
||||
*start = s;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/* Makes sure that ENTRY has a VALUE. */
|
||||
static gpg_error_t
|
||||
assert_value (pke_t entry)
|
||||
{
|
||||
size_t len;
|
||||
int swallow_ws;
|
||||
strlist_t s;
|
||||
char *p;
|
||||
|
||||
if (entry->value)
|
||||
return 0;
|
||||
|
||||
len = 0;
|
||||
swallow_ws = 0;
|
||||
for (s = entry->raw_value; s; s = s->next)
|
||||
len += continuation_length (s->d, &swallow_ws, NULL);
|
||||
|
||||
/* Add one for the terminating zero. */
|
||||
len += 1;
|
||||
|
||||
entry->value = p = xtrymalloc (len);
|
||||
if (entry->value == NULL)
|
||||
return gpg_error_from_syserror ();
|
||||
|
||||
swallow_ws = 0;
|
||||
for (s = entry->raw_value; s; s = s->next)
|
||||
{
|
||||
const char *start;
|
||||
size_t l = continuation_length (s->d, &swallow_ws, &start);
|
||||
|
||||
memcpy (p, start, l);
|
||||
p += l;
|
||||
}
|
||||
|
||||
*p++ = 0;
|
||||
assert (p - entry->value == len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Get the name. */
|
||||
char *
|
||||
pke_name (pke_t pke)
|
||||
{
|
||||
return pke->name;
|
||||
}
|
||||
|
||||
|
||||
/* Get the value. */
|
||||
char *
|
||||
pke_value (pke_t pke)
|
||||
{
|
||||
if (assert_value (pke))
|
||||
return NULL;
|
||||
return pke->value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Adding and modifying values. */
|
||||
|
||||
/* Add (NAME, VALUE, RAW_VALUE) to PK. NAME may be NULL for comments
|
||||
and blank lines. At least one of VALUE and RAW_VALUE must be
|
||||
given. If PRESERVE_ORDER is not given, entries with the same name
|
||||
are grouped. NAME, VALUE and RAW_VALUE is consumed. */
|
||||
static gpg_error_t
|
||||
_pkc_add (pkc_t pk, char *name, char *value, strlist_t raw_value,
|
||||
int preserve_order)
|
||||
{
|
||||
gpg_error_t err = 0;
|
||||
pke_t e;
|
||||
|
||||
assert (value || raw_value);
|
||||
|
||||
if (name && ! valid_name (name))
|
||||
{
|
||||
err = gpg_error (GPG_ERR_INV_NAME);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (name && strcasecmp (name, "Key:") == 0 && pkc_lookup (pk, "Key:"))
|
||||
{
|
||||
err = gpg_error (GPG_ERR_INV_NAME);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
e = xtrycalloc (1, sizeof *e);
|
||||
if (e == NULL)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
e->name = name;
|
||||
e->value = value;
|
||||
e->raw_value = raw_value;
|
||||
|
||||
if (pk->first)
|
||||
{
|
||||
pke_t last;
|
||||
|
||||
if (preserve_order)
|
||||
last = pk->last;
|
||||
else
|
||||
{
|
||||
/* See if there is already an entry with NAME. */
|
||||
last = pkc_lookup (pk, name);
|
||||
|
||||
/* If so, find the last in that block. */
|
||||
if (last)
|
||||
while (last->next)
|
||||
{
|
||||
pke_t next = last->next;
|
||||
|
||||
if (next->name && strcasecmp (next->name, name) == 0)
|
||||
last = next;
|
||||
else
|
||||
break;
|
||||
}
|
||||
/* Otherwise, just find the last entry. */
|
||||
else
|
||||
last = pk->last;
|
||||
}
|
||||
|
||||
if (last->next)
|
||||
{
|
||||
e->prev = last;
|
||||
e->next = last->next;
|
||||
last->next = e;
|
||||
e->next->prev = e;
|
||||
}
|
||||
else
|
||||
{
|
||||
e->prev = last;
|
||||
last->next = e;
|
||||
pk->last = e;
|
||||
}
|
||||
}
|
||||
else
|
||||
pk->first = pk->last = e;
|
||||
|
||||
leave:
|
||||
if (err)
|
||||
{
|
||||
xfree (name);
|
||||
if (value)
|
||||
wipememory (value, strlen (value));
|
||||
xfree (value);
|
||||
free_strlist_wipe (raw_value);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it
|
||||
is not updated but the new entry is appended. */
|
||||
gpg_error_t
|
||||
pkc_add (pkc_t pk, const char *name, const char *value)
|
||||
{
|
||||
char *k, *v;
|
||||
|
||||
k = xtrystrdup (name);
|
||||
if (k == NULL)
|
||||
return gpg_error_from_syserror ();
|
||||
|
||||
v = xtrystrdup (value);
|
||||
if (v == NULL)
|
||||
{
|
||||
xfree (k);
|
||||
return gpg_error_from_syserror ();
|
||||
}
|
||||
|
||||
return _pkc_add (pk, k, v, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it
|
||||
is updated with VALUE. If multiple entries with NAME exist, the
|
||||
first entry is updated. */
|
||||
gpg_error_t
|
||||
pkc_set (pkc_t pk, const char *name, const char *value)
|
||||
{
|
||||
pke_t e;
|
||||
|
||||
if (! valid_name (name))
|
||||
return GPG_ERR_INV_NAME;
|
||||
|
||||
e = pkc_lookup (pk, name);
|
||||
if (e)
|
||||
{
|
||||
char *v;
|
||||
|
||||
v = xtrystrdup (value);
|
||||
if (v == NULL)
|
||||
return gpg_error_from_syserror ();
|
||||
|
||||
free_strlist_wipe (e->raw_value);
|
||||
e->raw_value = NULL;
|
||||
if (e->value)
|
||||
wipememory (e->value, strlen (e->value));
|
||||
xfree (e->value);
|
||||
e->value = v;
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return pkc_add (pk, name, value);
|
||||
}
|
||||
|
||||
|
||||
/* Delete the given entry from PK. */
|
||||
void
|
||||
pkc_delete (pkc_t pk, pke_t entry)
|
||||
{
|
||||
if (entry->prev)
|
||||
entry->prev->next = entry->next;
|
||||
else
|
||||
pk->first = entry->next;
|
||||
|
||||
if (entry->next)
|
||||
entry->next->prev = entry->prev;
|
||||
else
|
||||
pk->last = entry->prev;
|
||||
|
||||
pke_release (entry);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Lookup and iteration. */
|
||||
|
||||
/* Get the first non-comment entry. */
|
||||
pke_t
|
||||
pkc_first (pkc_t pk)
|
||||
{
|
||||
pke_t entry;
|
||||
for (entry = pk->first; entry; entry = entry->next)
|
||||
if (entry->name)
|
||||
return entry;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Get the first entry with the given name. */
|
||||
pke_t
|
||||
pkc_lookup (pkc_t pk, const char *name)
|
||||
{
|
||||
pke_t entry;
|
||||
for (entry = pk->first; entry; entry = entry->next)
|
||||
if (entry->name && strcasecmp (entry->name, name) == 0)
|
||||
return entry;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Get the next non-comment entry. */
|
||||
pke_t
|
||||
pke_next (pke_t entry)
|
||||
{
|
||||
for (entry = entry->next; entry; entry = entry->next)
|
||||
if (entry->name)
|
||||
return entry;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Get the next entry with the given name. */
|
||||
pke_t
|
||||
pke_next_value (pke_t entry, const char *name)
|
||||
{
|
||||
for (entry = entry->next; entry; entry = entry->next)
|
||||
if (entry->name && strcasecmp (entry->name, name) == 0)
|
||||
return entry;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Private key handling. */
|
||||
|
||||
/* Get the private key. */
|
||||
gpg_error_t
|
||||
pkc_get_private_key (pkc_t pk, gcry_sexp_t *retsexp)
|
||||
{
|
||||
gpg_error_t err;
|
||||
pke_t e;
|
||||
|
||||
e = pkc_lookup (pk, "Key:");
|
||||
if (e == NULL)
|
||||
return gpg_error (GPG_ERR_MISSING_KEY);
|
||||
|
||||
err = assert_value (e);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return gcry_sexp_sscan (retsexp, NULL, e->value, strlen (e->value));
|
||||
}
|
||||
|
||||
|
||||
/* Set the private key. */
|
||||
gpg_error_t
|
||||
pkc_set_private_key (pkc_t pk, gcry_sexp_t sexp)
|
||||
{
|
||||
gpg_error_t err;
|
||||
char *raw, *clean, *p;
|
||||
size_t len, i;
|
||||
|
||||
len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
|
||||
|
||||
raw = xtrymalloc (len);
|
||||
if (raw == NULL)
|
||||
return gpg_error_from_syserror ();
|
||||
|
||||
clean = xtrymalloc (len);
|
||||
if (clean == NULL)
|
||||
{
|
||||
xfree (raw);
|
||||
return gpg_error_from_syserror ();
|
||||
}
|
||||
|
||||
gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, raw, len);
|
||||
|
||||
/* Strip any whitespace at the end. */
|
||||
i = strlen (raw) - 1;
|
||||
while (i && ascii_isspace (raw[i]))
|
||||
{
|
||||
raw[i] = 0;
|
||||
i--;
|
||||
}
|
||||
|
||||
/* Replace any newlines with spaces, remove superfluous whitespace. */
|
||||
len = strlen (raw);
|
||||
for (p = clean, i = 0; i < len; i++)
|
||||
{
|
||||
char c = raw[i];
|
||||
|
||||
/* Collapse contiguous and superfluous spaces. */
|
||||
if (ascii_isspace (c) && i > 0
|
||||
&& (ascii_isspace (raw[i-1]) || raw[i-1] == '(' || raw[i-1] == ')'))
|
||||
continue;
|
||||
|
||||
if (c == '\n')
|
||||
c = ' ';
|
||||
|
||||
*p++ = c;
|
||||
}
|
||||
*p = 0;
|
||||
|
||||
err = pkc_set (pk, "Key:", clean);
|
||||
xfree (raw);
|
||||
xfree (clean);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Parsing and serialization. */
|
||||
|
||||
/* Parse STREAM and return a newly allocated private key container
|
||||
structure in RESULT. If ERRLINEP is given, the line number the
|
||||
parser was last considering is stored there. */
|
||||
gpg_error_t
|
||||
pkc_parse (pkc_t *result, int *errlinep, estream_t stream)
|
||||
{
|
||||
gpg_error_t err = 0;
|
||||
gpgrt_ssize_t len;
|
||||
char *buf = NULL;
|
||||
size_t buf_len = 0;
|
||||
char *name = NULL;
|
||||
strlist_t raw_value = NULL;
|
||||
|
||||
|
||||
*result = pkc_new ();
|
||||
if (*result == NULL)
|
||||
return gpg_error_from_syserror ();
|
||||
|
||||
if (errlinep)
|
||||
*errlinep = 0;
|
||||
while ((len = es_read_line (stream, &buf, &buf_len, NULL)))
|
||||
{
|
||||
char *p;
|
||||
if (errlinep)
|
||||
*errlinep += 1;
|
||||
|
||||
/* Skip any whitespace. */
|
||||
for (p = buf; *p && ascii_isspace (*p); p++)
|
||||
/* Do nothing. */;
|
||||
|
||||
if (name && (spacep (buf) || *p == 0))
|
||||
{
|
||||
/* A continuation. */
|
||||
if (append_to_strlist_try (&raw_value, buf) == NULL)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* No continuation. Add the current entry if any. */
|
||||
if (raw_value)
|
||||
{
|
||||
err = _pkc_add (*result, name, NULL, raw_value, 1);
|
||||
if (err)
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* And prepare for the next one. */
|
||||
name = NULL;
|
||||
raw_value = NULL;
|
||||
|
||||
if (*p != 0 && *p != '#')
|
||||
{
|
||||
char *colon, *value, tmp;
|
||||
|
||||
colon = strchr (buf, ':');
|
||||
if (colon == NULL)
|
||||
{
|
||||
err = gpg_error (GPG_ERR_INV_VALUE);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
value = colon + 1;
|
||||
tmp = *value;
|
||||
*value = 0;
|
||||
name = xstrdup (p);
|
||||
*value = tmp;
|
||||
|
||||
if (name == NULL)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (append_to_strlist (&raw_value, value) == NULL)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (append_to_strlist (&raw_value, buf) == NULL)
|
||||
{
|
||||
err = gpg_error_from_syserror ();
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add the final entry. */
|
||||
if (raw_value)
|
||||
err = _pkc_add (*result, name, NULL, raw_value, 1);
|
||||
|
||||
leave:
|
||||
gpgrt_free (buf);
|
||||
if (err)
|
||||
{
|
||||
pkc_release (*result);
|
||||
*result = NULL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Write a representation of PK to STREAM. */
|
||||
gpg_error_t
|
||||
pkc_write (pkc_t pk, estream_t stream)
|
||||
{
|
||||
gpg_error_t err;
|
||||
pke_t entry;
|
||||
strlist_t s;
|
||||
|
||||
for (entry = pk->first; entry; entry = entry->next)
|
||||
{
|
||||
if (entry->name)
|
||||
es_fputs (entry->name, stream);
|
||||
|
||||
err = assert_raw_value (entry);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
for (s = entry->raw_value; s; s = s->next)
|
||||
es_fputs (s->d, stream);
|
||||
|
||||
if (es_ferror (stream))
|
||||
return gpg_error_from_syserror ();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
109
common/private-keys.h
Normal file
109
common/private-keys.h
Normal file
@ -0,0 +1,109 @@
|
||||
/* private-keys.h - Parser and writer for the extended private key format.
|
||||
* Copyright (C) 2016 g10 Code GmbH
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify
|
||||
* it under the terms of either
|
||||
*
|
||||
* - the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* or
|
||||
*
|
||||
* - the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* or both in parallel, as here.
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GNUPG_COMMON_PRIVATE_KEYS_H
|
||||
#define GNUPG_COMMON_PRIVATE_KEYS_H
|
||||
|
||||
struct private_key_container;
|
||||
typedef struct private_key_container *pkc_t;
|
||||
|
||||
struct private_key_entry;
|
||||
typedef struct private_key_entry *pke_t;
|
||||
|
||||
|
||||
|
||||
/* Memory management, and dealing with entries. */
|
||||
|
||||
/* Allocate a private key container structure. */
|
||||
pkc_t pkc_new (void);
|
||||
|
||||
/* Release a private key container structure. */
|
||||
void pkc_release (pkc_t pk);
|
||||
|
||||
/* Get the name. */
|
||||
char *pke_name (pke_t pke);
|
||||
|
||||
/* Get the value. */
|
||||
char *pke_value (pke_t pke);
|
||||
|
||||
|
||||
|
||||
/* Lookup and iteration. */
|
||||
|
||||
/* Get the first non-comment entry. */
|
||||
pke_t pkc_first (pkc_t pk);
|
||||
|
||||
/* Get the first entry with the given name. */
|
||||
pke_t pkc_lookup (pkc_t pk, const char *name);
|
||||
|
||||
/* Get the next non-comment entry. */
|
||||
pke_t pke_next (pke_t entry);
|
||||
|
||||
/* Get the next entry with the given name. */
|
||||
pke_t pke_next_value (pke_t entry, const char *name);
|
||||
|
||||
|
||||
|
||||
/* Adding and modifying values. */
|
||||
|
||||
/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it
|
||||
is not updated but the new entry is appended. */
|
||||
gpg_error_t pkc_add (pkc_t pk, const char *name, const char *value);
|
||||
|
||||
/* Add (NAME, VALUE) to PK. If an entry with NAME already exists, it
|
||||
is updated with VALUE. If multiple entries with NAME exist, the
|
||||
first entry is updated. */
|
||||
gpg_error_t pkc_set (pkc_t pk, const char *name, const char *value);
|
||||
|
||||
/* Delete the given entry from PK. */
|
||||
void pkc_delete (pkc_t pk, pke_t pke);
|
||||
|
||||
|
||||
|
||||
/* Private key handling. */
|
||||
|
||||
/* Get the private key. */
|
||||
gpg_error_t pkc_get_private_key (pkc_t pk, gcry_sexp_t *retsexp);
|
||||
|
||||
/* Set the private key. */
|
||||
gpg_error_t pkc_set_private_key (pkc_t pk, gcry_sexp_t sexp);
|
||||
|
||||
|
||||
|
||||
/* Parsing and serialization. */
|
||||
|
||||
/* Parse STREAM and return a newly allocated private key container
|
||||
structure in RESULT. If ERRLINEP is given, the line number the
|
||||
parser was last considering is stored there. */
|
||||
gpg_error_t pkc_parse (pkc_t *result, int *errlinep, estream_t stream);
|
||||
|
||||
/* Write a representation of PK to STREAM. */
|
||||
gpg_error_t pkc_write (pkc_t pk, estream_t stream);
|
||||
|
||||
#endif /* GNUPG_COMMON_PRIVATE_KEYS_H */
|
543
common/t-private-keys.c
Normal file
543
common/t-private-keys.c
Normal file
@ -0,0 +1,543 @@
|
||||
/* t-private-keys.c - Module test for private-keys.c
|
||||
* Copyright (C) 2016 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "private-keys.h"
|
||||
|
||||
static int verbose;
|
||||
|
||||
void
|
||||
test_getting_values (pkc_t pk)
|
||||
{
|
||||
pke_t e;
|
||||
|
||||
e = pkc_lookup (pk, "Comment:");
|
||||
assert (e);
|
||||
|
||||
/* Names are case-insensitive. */
|
||||
e = pkc_lookup (pk, "comment:");
|
||||
assert (e);
|
||||
e = pkc_lookup (pk, "COMMENT:");
|
||||
assert (e);
|
||||
|
||||
e = pkc_lookup (pk, "SomeOtherName:");
|
||||
assert (e);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
test_key_extraction (pkc_t pk)
|
||||
{
|
||||
gpg_error_t err;
|
||||
gcry_sexp_t key;
|
||||
|
||||
err = pkc_get_private_key (pk, &key);
|
||||
assert (err == 0);
|
||||
assert (key);
|
||||
|
||||
if (verbose)
|
||||
gcry_sexp_dump (key);
|
||||
|
||||
gcry_sexp_release (key);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
test_iteration (pkc_t pk)
|
||||
{
|
||||
int i;
|
||||
pke_t e;
|
||||
|
||||
i = 0;
|
||||
for (e = pkc_first (pk); e; e = pke_next (e))
|
||||
i++;
|
||||
assert (i == 4);
|
||||
|
||||
i = 0;
|
||||
for (e = pkc_lookup (pk, "Comment:");
|
||||
e;
|
||||
e = pke_next_value (e, "Comment:"))
|
||||
i++;
|
||||
assert (i == 3);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
test_whitespace (pkc_t pk)
|
||||
{
|
||||
pke_t e;
|
||||
|
||||
e = pkc_lookup (pk, "One:");
|
||||
assert (e);
|
||||
assert (strcmp (pke_value (e), "WithoutWhitespace") == 0);
|
||||
|
||||
e = pkc_lookup (pk, "Two:");
|
||||
assert (e);
|
||||
assert (strcmp (pke_value (e), "With Whitespace") == 0);
|
||||
|
||||
e = pkc_lookup (pk, "Three:");
|
||||
assert (e);
|
||||
assert (strcmp (pke_value (e),
|
||||
"Blank lines in continuations encode newlines.\n"
|
||||
"Next paragraph.") == 0);
|
||||
}
|
||||
|
||||
|
||||
struct
|
||||
{
|
||||
char *value;
|
||||
void (*test_func) (pkc_t);
|
||||
} tests[] =
|
||||
{
|
||||
{
|
||||
"# This is a comment followed by an empty line\n"
|
||||
"\n",
|
||||
NULL,
|
||||
},
|
||||
{
|
||||
"# This is a comment followed by two empty lines, Windows style\r\n"
|
||||
"\r\n"
|
||||
"\r\n",
|
||||
NULL,
|
||||
},
|
||||
{
|
||||
"# Some name,value pairs\n"
|
||||
"Comment: Some comment.\n"
|
||||
"SomeOtherName: Some value.\n",
|
||||
test_getting_values,
|
||||
},
|
||||
{
|
||||
" # Whitespace is preserved as much as possible\r\n"
|
||||
"Comment:Some comment.\n"
|
||||
"SomeOtherName: Some value. \n",
|
||||
test_getting_values,
|
||||
},
|
||||
{
|
||||
"# Values may be continued in the next line as indicated by leading\n"
|
||||
"# space\n"
|
||||
"Comment: Some rather long\n"
|
||||
" comment that is continued in the next line.\n"
|
||||
"\n"
|
||||
" Blank lines with or without whitespace are allowed within\n"
|
||||
" continuations to allow paragraphs.\n"
|
||||
"SomeOtherName: Some value.\n",
|
||||
test_getting_values,
|
||||
},
|
||||
{
|
||||
"# Names may be given multiple times forming an array of values\n"
|
||||
"Comment: Some comment, element 0.\n"
|
||||
"Comment: Some comment, element 1.\n"
|
||||
"Comment: Some comment, element 2.\n"
|
||||
"SomeOtherName: Some value.\n",
|
||||
test_iteration,
|
||||
},
|
||||
{
|
||||
"# One whitespace at the beginning of a continuation is swallowed.\n"
|
||||
"One: Without\n"
|
||||
" Whitespace\n"
|
||||
"Two: With\n"
|
||||
" Whitespace\n"
|
||||
"Three: Blank lines in continuations encode newlines.\n"
|
||||
"\n"
|
||||
" Next paragraph.\n",
|
||||
test_whitespace,
|
||||
},
|
||||
{
|
||||
"Description: Key to sign all GnuPG released tarballs.\n"
|
||||
" The key is actually stored on a smart card.\n"
|
||||
"Use-for-ssh: yes\n"
|
||||
"OpenSSH-cert: long base64 encoded string wrapped so that this\n"
|
||||
" key file can be easily edited with a standard editor.\n"
|
||||
"Key: (shadowed-private-key\n"
|
||||
" (rsa\n"
|
||||
" (n #00AA1AD2A55FD8C8FDE9E1941772D9CC903FA43B268CB1B5A1BAFDC900\n"
|
||||
" 2961D8AEA153424DC851EF13B83AC64FBE365C59DC1BD3E83017C90D4365B4\n"
|
||||
" 83E02859FC13DB5842A00E969480DB96CE6F7D1C03600392B8E08EF0C01FC7\n"
|
||||
" 19F9F9086B25AD39B4F1C2A2DF3E2BE317110CFFF21D4A11455508FE407997\n"
|
||||
" 601260816C8422297C0637BB291C3A079B9CB38A92CE9E551F80AA0EBF4F0E\n"
|
||||
" 72C3F250461E4D31F23A7087857FC8438324A013634563D34EFDDCBF2EA80D\n"
|
||||
" F9662C9CCD4BEF2522D8BDFED24CEF78DC6B309317407EAC576D889F88ADA0\n"
|
||||
" 8C4FFB480981FB68C5C6CA27503381D41018E6CDC52AAAE46B166BDC10637A\n"
|
||||
" E186A02BA2497FDC5D1221#)\n"
|
||||
" (e #00010001#)\n"
|
||||
" (shadowed t1-v1\n"
|
||||
" (#D2760001240102000005000011730000# OPENPGP.1)\n"
|
||||
" )))\n",
|
||||
test_key_extraction,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static char *
|
||||
pkc_to_string (pkc_t pk)
|
||||
{
|
||||
gpg_error_t err;
|
||||
char *buf;
|
||||
size_t len;
|
||||
estream_t sink;
|
||||
|
||||
sink = es_fopenmem (0, "rw");
|
||||
assert (sink);
|
||||
|
||||
err = pkc_write (pk, sink);
|
||||
assert (err == 0);
|
||||
|
||||
len = es_ftell (sink);
|
||||
buf = xmalloc (len+1);
|
||||
assert (buf);
|
||||
|
||||
es_fseek (sink, 0, SEEK_SET);
|
||||
es_read (sink, buf, len, NULL);
|
||||
buf[len] = 0;
|
||||
|
||||
es_fclose (sink);
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
void dummy_free (void *p) { (void) p; }
|
||||
void *dummy_realloc (void *p, size_t s) { (void) s; return p; }
|
||||
|
||||
void
|
||||
run_tests (void)
|
||||
{
|
||||
gpg_error_t err;
|
||||
pkc_t pk;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < DIM (tests); i++)
|
||||
{
|
||||
estream_t source;
|
||||
char *buf;
|
||||
size_t len;
|
||||
|
||||
len = strlen (tests[i].value);
|
||||
source = es_mopen (tests[i].value, len, len,
|
||||
0, dummy_realloc, dummy_free, "r");
|
||||
assert (source);
|
||||
|
||||
err = pkc_parse (&pk, NULL, source);
|
||||
assert (err == 0);
|
||||
assert (pk);
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
err = pkc_write (pk, es_stderr);
|
||||
assert (err == 0);
|
||||
}
|
||||
|
||||
buf = pkc_to_string (pk);
|
||||
assert (memcmp (tests[i].value, buf, len) == 0);
|
||||
|
||||
es_fclose (source);
|
||||
xfree (buf);
|
||||
|
||||
if (tests[i].test_func)
|
||||
tests[i].test_func (pk);
|
||||
|
||||
pkc_release (pk);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
run_modification_tests (void)
|
||||
{
|
||||
gpg_error_t err;
|
||||
pkc_t pk;
|
||||
pke_t e;
|
||||
gcry_sexp_t key;
|
||||
char *buf;
|
||||
|
||||
pk = pkc_new ();
|
||||
assert (pk);
|
||||
|
||||
pkc_set (pk, "Foo:", "Bar");
|
||||
buf = pkc_to_string (pk);
|
||||
assert (strcmp (buf, "Foo: Bar\n") == 0);
|
||||
xfree (buf);
|
||||
|
||||
pkc_set (pk, "Foo:", "Baz");
|
||||
buf = pkc_to_string (pk);
|
||||
assert (strcmp (buf, "Foo: Baz\n") == 0);
|
||||
xfree (buf);
|
||||
|
||||
pkc_set (pk, "Bar:", "Bazzel");
|
||||
buf = pkc_to_string (pk);
|
||||
assert (strcmp (buf, "Foo: Baz\nBar: Bazzel\n") == 0);
|
||||
xfree (buf);
|
||||
|
||||
pkc_add (pk, "Foo:", "Bar");
|
||||
buf = pkc_to_string (pk);
|
||||
assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\n") == 0);
|
||||
xfree (buf);
|
||||
|
||||
pkc_add (pk, "DontExistYet:", "Bar");
|
||||
buf = pkc_to_string (pk);
|
||||
assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\nDontExistYet: Bar\n")
|
||||
== 0);
|
||||
xfree (buf);
|
||||
|
||||
pkc_delete (pk, pkc_lookup (pk, "DontExistYet:"));
|
||||
buf = pkc_to_string (pk);
|
||||
assert (strcmp (buf, "Foo: Baz\nFoo: Bar\nBar: Bazzel\n") == 0);
|
||||
xfree (buf);
|
||||
|
||||
pkc_delete (pk, pke_next_value (pkc_lookup (pk, "Foo:"), "Foo:"));
|
||||
buf = pkc_to_string (pk);
|
||||
assert (strcmp (buf, "Foo: Baz\nBar: Bazzel\n") == 0);
|
||||
xfree (buf);
|
||||
|
||||
pkc_delete (pk, pkc_lookup (pk, "Foo:"));
|
||||
buf = pkc_to_string (pk);
|
||||
assert (strcmp (buf, "Bar: Bazzel\n") == 0);
|
||||
xfree (buf);
|
||||
|
||||
pkc_delete (pk, pkc_first (pk));
|
||||
buf = pkc_to_string (pk);
|
||||
assert (strcmp (buf, "") == 0);
|
||||
xfree (buf);
|
||||
|
||||
pkc_set (pk, "Foo:", "A really long value spanning across multiple lines"
|
||||
" that has to be wrapped at a convenient space.");
|
||||
buf = pkc_to_string (pk);
|
||||
assert (strcmp (buf, "Foo: A really long value spanning across multiple"
|
||||
" lines that has to be\n wrapped at a convenient space.\n")
|
||||
== 0);
|
||||
xfree (buf);
|
||||
|
||||
pkc_set (pk, "Foo:", "XA really long value spanning across multiple lines"
|
||||
" that has to be wrapped at a convenient space.");
|
||||
buf = pkc_to_string (pk);
|
||||
assert (strcmp (buf, "Foo: XA really long value spanning across multiple"
|
||||
" lines that has to\n be wrapped at a convenient space.\n")
|
||||
== 0);
|
||||
xfree (buf);
|
||||
|
||||
pkc_set (pk, "Foo:", "XXXXA really long value spanning across multiple lines"
|
||||
" that has to be wrapped at a convenient space.");
|
||||
buf = pkc_to_string (pk);
|
||||
assert (strcmp (buf, "Foo: XXXXA really long value spanning across multiple"
|
||||
" lines that has\n to be wrapped at a convenient space.\n")
|
||||
== 0);
|
||||
xfree (buf);
|
||||
|
||||
pkc_set (pk, "Foo:", "Areallylongvaluespanningacrossmultiplelines"
|
||||
"thathastobewrappedataconvenientspacethatisnotthere.");
|
||||
buf = pkc_to_string (pk);
|
||||
assert (strcmp (buf, "Foo: Areallylongvaluespanningacrossmultiplelinesthat"
|
||||
"hastobewrappedataco\n nvenientspacethatisnotthere.\n")
|
||||
== 0);
|
||||
xfree (buf);
|
||||
pkc_release (pk);
|
||||
|
||||
pk = pkc_new ();
|
||||
assert (pk);
|
||||
|
||||
err = gcry_sexp_build (&key, NULL, "(hello world)");
|
||||
assert (err == 0);
|
||||
assert (key);
|
||||
|
||||
err = pkc_set_private_key (pk, key);
|
||||
gcry_sexp_release (key);
|
||||
assert (err == 0);
|
||||
buf = pkc_to_string (pk);
|
||||
assert (strcmp (buf, "Key: (hello world)\n") == 0);
|
||||
xfree (buf);
|
||||
pkc_release (pk);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
convert (const char *fname)
|
||||
{
|
||||
gpg_error_t err;
|
||||
estream_t source;
|
||||
gcry_sexp_t key;
|
||||
char *buf;
|
||||
size_t buflen;
|
||||
gpgrt_ssize_t nread;
|
||||
struct stat st;
|
||||
pkc_t pk;
|
||||
|
||||
source = es_fopen (fname, "rb");
|
||||
if (source == NULL)
|
||||
goto leave;
|
||||
|
||||
if (fstat (es_fileno (source), &st))
|
||||
goto leave;
|
||||
|
||||
buflen = st.st_size;
|
||||
buf = xtrymalloc (buflen+1);
|
||||
assert (buf);
|
||||
|
||||
if (es_fread (buf, buflen, 1, source) != 1)
|
||||
goto leave;
|
||||
|
||||
err = gcry_sexp_sscan (&key, NULL, buf, buflen);
|
||||
if (err)
|
||||
{
|
||||
fprintf (stderr, "malformed s-expression in %s\n", fname);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
pk = pkc_new ();
|
||||
assert (pk);
|
||||
|
||||
err = pkc_set_private_key (pk, key);
|
||||
assert (err == 0);
|
||||
|
||||
err = pkc_write (pk, es_stdout);
|
||||
assert (err == 0);
|
||||
|
||||
return;
|
||||
|
||||
leave:
|
||||
perror (fname);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
parse (const char *fname)
|
||||
{
|
||||
gpg_error_t err;
|
||||
estream_t source;
|
||||
char *buf;
|
||||
pkc_t pk_a, pk_b;
|
||||
pke_t e;
|
||||
int line;
|
||||
|
||||
source = es_fopen (fname, "rb");
|
||||
if (source == NULL)
|
||||
{
|
||||
perror (fname);
|
||||
exit (1);
|
||||
}
|
||||
|
||||
err = pkc_parse (&pk_a, &line, source);
|
||||
if (err)
|
||||
{
|
||||
fprintf (stderr, "failed to parse %s line %d: %s\n",
|
||||
fname, line, gpg_strerror (err));
|
||||
exit (1);
|
||||
}
|
||||
|
||||
buf = pkc_to_string (pk_a);
|
||||
xfree (buf);
|
||||
|
||||
pk_b = pkc_new ();
|
||||
assert (pk_b);
|
||||
|
||||
for (e = pkc_first (pk_a); e; e = pke_next (e))
|
||||
{
|
||||
gcry_sexp_t key = NULL;
|
||||
|
||||
if (strcasecmp (pke_name (e), "Key:") == 0)
|
||||
{
|
||||
err = pkc_get_private_key (pk_a, &key);
|
||||
if (err)
|
||||
key = NULL;
|
||||
}
|
||||
|
||||
if (key)
|
||||
{
|
||||
err = pkc_set_private_key (pk_b, key);
|
||||
assert (err == 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
err = pkc_add (pk_b, pke_name (e), pke_value (e));
|
||||
assert (err == 0);
|
||||
}
|
||||
}
|
||||
|
||||
buf = pkc_to_string (pk_b);
|
||||
if (verbose)
|
||||
fprintf (stdout, "%s", buf);
|
||||
xfree (buf);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
print_usage (void)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"usage: t-private-keys [--verbose]"
|
||||
" [--convert <private-key-file>"
|
||||
" || --parse <extended-private-key-file>]\n");
|
||||
exit (2);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
enum { TEST, CONVERT, PARSE } command = TEST;
|
||||
|
||||
if (argc)
|
||||
{ argc--; argv++; }
|
||||
if (argc && !strcmp (argv[0], "--verbose"))
|
||||
{
|
||||
verbose = 1;
|
||||
argc--; argv++;
|
||||
}
|
||||
|
||||
if (argc && !strcmp (argv[0], "--convert"))
|
||||
{
|
||||
command = CONVERT;
|
||||
argc--; argv++;
|
||||
if (argc != 1)
|
||||
print_usage ();
|
||||
}
|
||||
|
||||
if (argc && !strcmp (argv[0], "--parse"))
|
||||
{
|
||||
command = PARSE;
|
||||
argc--; argv++;
|
||||
if (argc != 1)
|
||||
print_usage ();
|
||||
}
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case TEST:
|
||||
run_tests ();
|
||||
run_modification_tests ();
|
||||
break;
|
||||
|
||||
case CONVERT:
|
||||
convert (*argv);
|
||||
break;
|
||||
|
||||
case PARSE:
|
||||
parse (*argv);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -333,6 +333,9 @@ int _gnupg_isatty (int fd);
|
||||
/*-- Macros to replace ctype ones to avoid locale problems. --*/
|
||||
#define spacep(p) (*(p) == ' ' || *(p) == '\t')
|
||||
#define digitp(p) (*(p) >= '0' && *(p) <= '9')
|
||||
#define alphap(p) ((*(p) >= 'A' && *(p) <= 'Z') \
|
||||
|| (*(p) >= 'a' && *(p) <= 'z'))
|
||||
#define alnump(p) (alphap (p) || digitp (p))
|
||||
#define hexdigitp(a) (digitp (a) \
|
||||
|| (*(a) >= 'A' && *(a) <= 'F') \
|
||||
|| (*(a) >= 'a' && *(a) <= 'f'))
|
||||
|
@ -28,11 +28,17 @@ AM_CFLAGS =
|
||||
|
||||
TESTS_ENVIRONMENT = GPG_AGENT_INFO= LC_ALL=C
|
||||
|
||||
TESTS = from-classic.test
|
||||
TESTS = from-classic.test \
|
||||
extended-private-key-format.test
|
||||
|
||||
TEST_FILES = from-classic.gpghome/pubring.gpg.asc \
|
||||
from-classic.gpghome/secring.gpg.asc \
|
||||
from-classic.gpghome/trustdb.gpg.asc
|
||||
from-classic.gpghome/trustdb.gpg.asc \
|
||||
extended-private-key-format.gpghome/trustdb.gpg.asc \
|
||||
extended-private-key-format.gpghome/pubring.kbx.asc \
|
||||
extended-private-key-format.gpghome/private-keys-v1.d/13FDB8809B17C5547779F9D205C45F47CE0217CE.key.asc \
|
||||
extended-private-key-format.gpghome/private-keys-v1.d/343D8AF79796EE107D645A2787A9D9252F924E6F.key.asc \
|
||||
extended-private-key-format.gpghome/private-keys-v1.d/8B5ABF3EF9EB8D96B91A0B8C2C4401C91C834C34.key.asc
|
||||
|
||||
EXTRA_DIST = $(TESTS) $(TEST_FILES)
|
||||
|
||||
|
@ -0,0 +1,27 @@
|
||||
-----BEGIN PGP ARMORED FILE-----
|
||||
Version: GnuPG v2
|
||||
Comment: Use "gpg --dearmor" for unpacking
|
||||
|
||||
S2V5OiAocHJpdmF0ZS1rZXkgKHJzYSAobiAjMDBBODUyNTY3NkVDRTRENzVGRTZE
|
||||
MDA3M0YyQkY5OUE2RjQ5MzNDRUJERDQKIDUyOEFGNTZFNEM2MUUyRjczMTI0NzM5
|
||||
MzY0NUREQUY1OEVBREQ1NjUyOUMyNTM5Nzc4MjM2NDYzREYyRDQ1ODUyMEU4MEUK
|
||||
IDM0QzA1ODI0MkIzRkY4OEREMzlBODgzQjQ3NUI2NkNFQUFCQkM5OTg5RkYwMUZG
|
||||
RTczNzY2MEU5QjYxQkI5REM5MTIwNUQKIDQyOEZGRkU4RjY3NUZBRUY2MTM2NThD
|
||||
NzJEQTZENzUwQzBFQkM0MEFGNjIzRDIwNjY5MkM4MjUxNEM0MDREODgyNUFCNzAK
|
||||
IDEwMDEjKShlICMwMTAxIykoZCAjMDBCQ0EwMDE0NDg1RkI3NkQ1MEU5QjZDQkE1
|
||||
NzIxQUMxMTIxMzkwRjg2MDhENDA4NEIKIEQwNDVBODc2REYzODEwRjExNEJDMkQ2
|
||||
OEVCNTUyRTYxQjAxRURCQzI0ODFGMDhDODI4MzJFMDBFMjc5RDY3QTg1MzA1NUQK
|
||||
IENBRTVDMjM1Njg1MUNCRTM2RDYxMEM0RDJBQjQzRkE2NTU5ODVDNDQ2OUQxRDkx
|
||||
MUUxQUZEODE3RUFBNUZFRTBGRjI2NTcKIDRDMzU5RTE3NTI4NzA1MjE5NDUzQjUx
|
||||
QUVDMTBEQkY3NTYyQjA2MUQ1QzY2QzM1QkIzRjlGMEIyMjJCOUQxOTZCOSMpKHAK
|
||||
ICAjMDBDMzNDNTgwNjM5OTZCRDU5NzUyQUFCREZEQUNEQUE3QjRCNjZBQTE3NTRF
|
||||
RTBEODlCNzc5NEYwREU4RkY3MjRDNTQKIDlGRjExMkEzMzI5MkJCOUQ3QkNFRTc5
|
||||
NEYwODAyNEMzRTU1RkQ4MjMzRjUwNzlFRDQ5OTFDNERGMjYxOEQ5IykocSAjMDAK
|
||||
IERDQjU5NDVGMDBGMUFGNDM4QkQ0QzMxMUI4QkFDQTNEOURCMEFEMTY1MTk4NjUz
|
||||
NDIwMzBGMURGMzA1N0U1NTMyQzQ3RjUKIDhEMzMwM0NCQTNDOEEyOTgxNEY2MTdC
|
||||
N0IzREVFOThGQUFBQUVFODExQjQ5OEZBQUYyMTc3Qjc3NjkjKSh1ICMyOUZCMkQK
|
||||
IEY2OUIyMzVBNDlBOTA2QjEwRUY3RDhGODFBQUVBOEFEODFFN0NEREUxRjRBNzlD
|
||||
RTI0NEJGOEZDRTZERDVFQjE4MTFCMEIKIEQ1RTUxNjVCOTU3MDg1MDM2OTAxREQy
|
||||
ODVBNjI4QzI5N0E3ODJEQTgxNTczQTQzRDFDMDkjKSkpCg==
|
||||
=laTh
|
||||
-----END PGP ARMORED FILE-----
|
@ -0,0 +1,17 @@
|
||||
-----BEGIN PGP ARMORED FILE-----
|
||||
Version: GnuPG v2
|
||||
Comment: Use "gpg --dearmor" for unpacking
|
||||
|
||||
KDExOnByaXZhdGUta2V5KDM6ZHNhKDE6cDEyOToArHGqWD0rP0Nn/c3nYELTD4m1
|
||||
gqR7f2+l1ZUMdHcweYwn/fVjaJKmbR+9GzeHWP398FWYs5mCU1DIfrZLF0nJnAJ6
|
||||
WRnN9TL+oub1BqqLvCmDSngRuZZ2gUX8DVmD8xTsPnDnG74QDUnvtnpDIAs32sg5
|
||||
dnusstrriXD8xXgt0g8pKDE6cTIxOgC449htJbbp5rkJHvBDs4YxEIkk5ykoMTpn
|
||||
MTI4Ol+ITxpSMOT5R67Bu4XWoYU7nVeYURpb6LJ8LK2CV7ygECwFdRFdukiGFB+a
|
||||
TP8nF6xtuXalaBuerkKp4QXVKqOIkp7MWN2TAOOg9eERHPT//whryf49meNYMPLv
|
||||
KAe60udHY76Glm+Zso+24WnEwXX2od1PHVV3CItWRb7YmhgGKSgxOnkxMjg6AgXt
|
||||
40h2lpiIHTjbu6fiCBzbr5j2eQX3cNoydkRphJ66bqD+DsPW/Ag0WBCQxgRaLgMr
|
||||
db64fQT+fyjbTBLbC8ytt5hpCbm/q5x3TTXDAUNjoB3CnA/tQItBy7qqq/A0d3FZ
|
||||
grr6AixK58uZ4wauy8LRZCph67UZ8akcgwJkmVkpKDE6eDIwOn/Y1rjZASGMK9IG
|
||||
b1y/ZDKT0zkTKSkp
|
||||
=muRa
|
||||
-----END PGP ARMORED FILE-----
|
@ -0,0 +1,20 @@
|
||||
-----BEGIN PGP ARMORED FILE-----
|
||||
Version: GnuPG v2
|
||||
Comment: Use "gpg --dearmor" for unpacking
|
||||
|
||||
S2V5OiAocHJpdmF0ZS1rZXkgKGVsZyAocCAjMDBDQ0Q4QjFGOURBQzc0RDgwOEND
|
||||
NTJGMEQ4OTQ2NERBNTU0QzY5RDY3RjMKIDMyM0M0MkE5NUM5OTYyREY0MjEyNkVD
|
||||
MEUwOTcxRjQ5QjgxMTUyOUE2QTJBRTlGMEFERUI4MzlBNjM0NjE1Q0Q1NkZBNTQK
|
||||
IEY1QTBCN0VGMjVBMEUyRkU4NDNGQTJFNkUwMjFDQUI0MTE5RTYwMzk0QzlENkEz
|
||||
RjdBRDRGNTc3OTZEMzY2NjlBNTEyNjYKIEMyN0E4RDFDNUE2QjQxNDFENUM4MzFF
|
||||
ODQ1NDFGM0M4MTFFODkwNzg5ODAzMzgyOTVGODJCN0Y3RkQ0MzMzRUZEOTMzMTIK
|
||||
IEYyQUIjKShnICMwNiMpKHkgIzM3NzNBNkQ5RUM4ODlENzZFMzI0RDZFNUVDMjFC
|
||||
RDQ1Njk5ODMxQUU0RkQwQUUwMzc4MjAKIDVCQUU1QjhDRTg1RkFEQUJEN0U2QjdD
|
||||
NzMwMjVDQjNENzMwRDVDNTgyOTAzNEQ3NkJFMDg1NUMyRTlGRjdBNDkyM0VGRkEK
|
||||
IEYxNkE5NjY2OTQ0REJDNjI5NDgzOEZDM0YwOUZGOTY0QThEMDIzQ0I4RUJBMzMy
|
||||
RkIwNTFFQTAyODIwRUU2MTIwRkZCRTYKIDJCMzZBMjAyQjNDNzUyRjlEQTc2QjJF
|
||||
QzExQTY3RDJFMzVFNjZFQzEwNjM1ODdCMjI1MDBFOEE0NkQxNTdCNzUjKSh4ICMK
|
||||
IDY5MTVDNkNFRDI1ODE0M0Y4OTM3QjEzMzVGNDg4N0YwMDQyQjdDNjMwMDUzOThG
|
||||
OTM5NkJCODUzMjM4Q0I2IykpKQo=
|
||||
=6fkh
|
||||
-----END PGP ARMORED FILE-----
|
@ -0,0 +1,39 @@
|
||||
-----BEGIN PGP ARMORED FILE-----
|
||||
Version: GnuPG v2
|
||||
Comment: Use "gpg --dearmor" for unpacking
|
||||
|
||||
AAAAIAEBAAJLQlhmAAAAAFcYtiNXGLYjAAAAAAAAAAAAAAQXAgEAAAAAAH4AAAOF
|
||||
AAIAHMHeuzTqi3EAnq+kdJc9UOHED97PAAAAIAAAAADNPQ9XAcv8rLKkkHMFo3iH
|
||||
snkHqgAAADwAAAAAAAAAAQAMAAACJQAAACIAAAAAAAIABP//////////AAAAAAAA
|
||||
AAAAAAAAVxi2IwAAAACZAaIEP/JSaxEEAKxxqlg9Kz9DZ/3N52BC0w+JtYKke39v
|
||||
pdWVDHR3MHmMJ/31Y2iSpm0fvRs3h1j9/fBVmLOZglNQyH62SxdJyZwCelkZzfUy
|
||||
/qLm9Qaqi7wpg0p4EbmWdoFF/A1Zg/MU7D5w5xu+EA1J77Z6QyALN9rIOXZ7rLLa
|
||||
64lw/MV4LdIPAKC449htJbbp5rkJHvBDs4YxEIkk5wP/X4hPGlIw5PlHrsG7hdah
|
||||
hTudV5hRGlvosnwsrYJXvKAQLAV1EV26SIYUH5pM/ycXrG25dqVoG56uQqnhBdUq
|
||||
o4iSnsxY3ZMA46D14REc9P//CGvJ/j2Z41gw8u8oB7rS50djvoaWb5myj7bhacTB
|
||||
dfah3U8dVXcIi1ZFvtiaGAYD+gIF7eNIdpaYiB0427un4ggc26+Y9nkF93DaMnZE
|
||||
aYSeum6g/g7D1vwINFgQkMYEWi4DK3W+uH0E/n8o20wS2wvMrbeYaQm5v6ucd001
|
||||
wwFDY6AdwpwP7UCLQcu6qqvwNHdxWYK6+gIsSufLmeMGrsvC0WQqYeu1GfGpHIMC
|
||||
ZJlZtCJUZXN0IHR3byAobm8gcHApIDx0d29AZXhhbXBsZS5jb20+iF8EExECAB8F
|
||||
Aj/yUmsCGwMHCwkIBwMCAQMVAgMDFgIBAh4BAheAAAoJEJc9UOHED97PgEMAn0F8
|
||||
RGDrnmXv7rqM2+pic2oDz1kpAJ0SWPHxdjJHWzoGMrHqocAy/3wFi7kBDQQ/8lJv
|
||||
EAQAzNix+drHTYCMxS8NiUZNpVTGnWfzMjxCqVyZYt9CEm7A4JcfSbgRUppqKunw
|
||||
reuDmmNGFc1W+lT1oLfvJaDi/oQ/oubgIcq0EZ5gOUydaj961PV3ltNmaaUSZsJ6
|
||||
jRxaa0FB1cgx6EVB88gR6JB4mAM4KV+Ct/f9QzPv2TMS8qsAAwYD/jdzptnsiJ12
|
||||
4yTW5ewhvUVpmDGuT9CuA3ggW65bjOhfravX5rfHMCXLPXMNXFgpA012vghVwun/
|
||||
ekkj7/rxapZmlE28YpSDj8Pwn/lkqNAjy466My+wUeoCgg7mEg/75is2ogKzx1L5
|
||||
2nay7BGmfS415m7BBjWHsiUA6KRtFXt1iEkEGBECAAkFAj/yUm8CGwwACgkQlz1Q
|
||||
4cQP3s8svgCgmWcpVwvtDN3nAVT1dMFTvCz0hfwAoI4VszJBesG/8GyLW+e2E+Li
|
||||
QXVqciq2GGJ3Ap2KvoCwCL/DhCAfcGsAAAHgAgEAAAAAAF4AAAFuAAEAHM8jSQsP
|
||||
eLhQu7xzadEgtibsq/UdAAAAIAAAAAAAAAABAAwAAADvAAAAJgAAAAAAAQAE////
|
||||
/wAAAAAAAAAAAAAAAFcYtkkAAAAAmQCMBD/yU70BBACoUlZ27OTXX+bQBz8r+Zpv
|
||||
STPOvdRSivVuTGHi9zEkc5NkXdr1jq3VZSnCU5d4I2Rj3y1FhSDoDjTAWCQrP/iN
|
||||
05qIO0dbZs6qu8mYn/Af/nN2YOm2G7nckSBdQo//6PZ1+u9hNljHLabXUMDrxAr2
|
||||
I9IGaSyCUUxATYglq3AQAQAJAQG0JlRlc3QgdGhyZWUgKG5vIHBwKSA8dGhyZWVA
|
||||
ZXhhbXBsZS5jb20+iLUEEwECAB8FAj/yU70CGwMHCwkIBwMCAQMVAgMDFgIBAh4B
|
||||
AheAAAoJENEgtibsq/UdakMD/2wg19VhpNbtM5CiVif1V57h945OmXr5Lh2SAsI5
|
||||
agMb9XXuT9yXsmv+JD5hEE6LRL98XAwGfvaQS9062aJQCocZAWdPJeEEsu+pMn/I
|
||||
QdHqGdkr7Oy6xjwSa+gh19JMg4mqR4AIQSkKvRoTSqSAGbi+gytnTmkA7aEUltog
|
||||
dYeJLGB5MYPnSPwADYVfNtLxsKZESLA=
|
||||
=tULv
|
||||
-----END PGP ARMORED FILE-----
|
@ -0,0 +1,31 @@
|
||||
-----BEGIN PGP ARMORED FILE-----
|
||||
Version: GnuPG v2
|
||||
Comment: Use "gpg --dearmor" for unpacking
|
||||
|
||||
AWdwZwMDAQUBAgAAVxi2IwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQoAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
CgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
CgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
CgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
CgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
=eBUi
|
||||
-----END PGP ARMORED FILE-----
|
57
tests/migrations/extended-private-key-format.test
Executable file
57
tests/migrations/extended-private-key-format.test
Executable file
@ -0,0 +1,57 @@
|
||||
#!/bin/sh
|
||||
# Copyright 2016 g10 Code GmbH
|
||||
#
|
||||
# This file is free software; as a special exception the author gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved. This file is
|
||||
# distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY, to the extent permitted by law; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
if [ -z "$srcdir" ]; then
|
||||
echo "not called from make" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
unset GNUPGHOME
|
||||
set -e
|
||||
|
||||
# (We may not use a relative name for gpg-agent.)
|
||||
GPG_AGENT="$(cd ../../agent && /bin/pwd)/gpg-agent"
|
||||
GPG="../../g10/gpg --no-permission-warning --no-greeting --no-secmem-warning
|
||||
--batch --agent-program=${GPG_AGENT}|--debug-quick-random"
|
||||
|
||||
TEST="extended-private-key-format"
|
||||
|
||||
setup_home()
|
||||
{
|
||||
XGNUPGHOME="`mktemp -d`"
|
||||
mkdir -p "$XGNUPGHOME/private-keys-v1.d"
|
||||
for F in $srcdir/$TEST.gpghome/*.asc; do
|
||||
$GPG --dearmor <"$F" >"$XGNUPGHOME/`basename $F .asc`"
|
||||
done
|
||||
for F in $srcdir/$TEST.gpghome/private-keys-v1.d/*.asc; do
|
||||
$GPG --dearmor <"$F" >"$XGNUPGHOME/private-keys-v1.d/`basename $F .asc`"
|
||||
done
|
||||
chmod go-rwx $XGNUPGHOME/* $XGNUPGHOME/*/*
|
||||
export GNUPGHOME="$XGNUPGHOME"
|
||||
}
|
||||
|
||||
cleanup_home()
|
||||
{
|
||||
rm -rf -- "$XGNUPGHOME"
|
||||
}
|
||||
|
||||
assert_keys_usable()
|
||||
{
|
||||
for KEY in C40FDECF ECABF51D; do
|
||||
$GPG --list-secret-keys $KEY >/dev/null
|
||||
done
|
||||
}
|
||||
|
||||
setup_home
|
||||
assert_keys_usable
|
||||
cleanup_home
|
||||
|
||||
|
||||
# XXX try changing a key, and check that the format is not changed.
|
Loading…
x
Reference in New Issue
Block a user