mirror of
git://git.gnupg.org/gnupg.git
synced 2025-01-03 12:11:33 +01:00
agent: Implement new protection mode openpgp-s2k3-ocb-aes.
* agent/protect.c (agent_protect): Add arg use_ocb. Change all caller to pass -1 for default. * agent/protect-tool.c: New option --debug-use-ocb. (oDebugUseOCB): New. (opt_debug_use_ocb): New. (main): Set option. (read_and_protect): Implement option. * agent/protect.c (OCB_MODE_SUPPORTED): New macro. (PROT_DEFAULT_TO_OCB): New macro. (do_encryption): Add args use_ocb, hashbegin, hashlen, timestamp_exp, and timestamp_exp_len. Implement OCB. (agent_protect): Change to support OCB. (do_decryption): Add new args is_ocb, aadhole_begin, and aadhole_len. Implement OCB. (merge_lists): Allow NULL for sha1hash. (agent_unprotect): Change to support OCB. (agent_private_key_type): Remove debug output. -- Instead of using the old OpenPGP way of appending a hash of the plaintext and encrypt that along with the plaintext, the new scheme uses a proper authenticated encryption mode. See keyformat.txt for a description. Libgcrypt 1.7 is required. This mode is not yet enabled because there would be no way to return to an older GnuPG version. To test the new scheme use gpg-protect-tool: ./gpg-protect-tool -av -P abc -p --debug-use-ocb <plain.key >prot.key ./gpg-protect-tool -av -P abc -u <prot.key Any key from the private key storage should work. Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
parent
100b413d7f
commit
4159567f7e
@ -471,7 +471,7 @@ unsigned long get_standard_s2k_count (void);
|
|||||||
unsigned char get_standard_s2k_count_rfc4880 (void);
|
unsigned char get_standard_s2k_count_rfc4880 (void);
|
||||||
int agent_protect (const unsigned char *plainkey, const char *passphrase,
|
int agent_protect (const unsigned char *plainkey, const char *passphrase,
|
||||||
unsigned char **result, size_t *resultlen,
|
unsigned char **result, size_t *resultlen,
|
||||||
unsigned long s2k_count);
|
unsigned long s2k_count, int use_ocb);
|
||||||
int agent_unprotect (ctrl_t ctrl,
|
int agent_unprotect (ctrl_t ctrl,
|
||||||
const unsigned char *protectedkey, const char *passphrase,
|
const unsigned char *protectedkey, const char *passphrase,
|
||||||
gnupg_isotime_t protected_at,
|
gnupg_isotime_t protected_at,
|
||||||
|
@ -3125,7 +3125,7 @@ ssh_key_to_protected_buffer (gcry_sexp_t key, const char *passphrase,
|
|||||||
gcry_sexp_sprint (key, GCRYSEXP_FMT_CANON, buffer_new, buffer_new_n);
|
gcry_sexp_sprint (key, GCRYSEXP_FMT_CANON, buffer_new, buffer_new_n);
|
||||||
/* FIXME: guarantee? */
|
/* FIXME: guarantee? */
|
||||||
|
|
||||||
err = agent_protect (buffer_new, passphrase, buffer, buffer_n, 0);
|
err = agent_protect (buffer_new, passphrase, buffer, buffer_n, 0, -1);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|
||||||
|
@ -2140,7 +2140,7 @@ cmd_import_key (assuan_context_t ctx, char *line)
|
|||||||
if (passphrase)
|
if (passphrase)
|
||||||
{
|
{
|
||||||
err = agent_protect (key, passphrase, &finalkey, &finalkeylen,
|
err = agent_protect (key, passphrase, &finalkey, &finalkeylen,
|
||||||
ctrl->s2k_count);
|
ctrl->s2k_count, -1);
|
||||||
if (!err)
|
if (!err)
|
||||||
err = agent_write_private_key (grip, finalkey, finalkeylen, force);
|
err = agent_write_private_key (grip, finalkey, finalkeylen, force);
|
||||||
}
|
}
|
||||||
|
@ -1066,7 +1066,7 @@ convert_from_openpgp_native (ctrl_t ctrl,
|
|||||||
|
|
||||||
if (!agent_protect (*r_key, passphrase,
|
if (!agent_protect (*r_key, passphrase,
|
||||||
&protectedkey, &protectedkeylen,
|
&protectedkey, &protectedkeylen,
|
||||||
ctrl->s2k_count))
|
ctrl->s2k_count, -1))
|
||||||
agent_write_private_key (grip, protectedkey, protectedkeylen, 1);
|
agent_write_private_key (grip, protectedkey, protectedkeylen, 1);
|
||||||
xfree (protectedkey);
|
xfree (protectedkey);
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ store_key (gcry_sexp_t private, const char *passphrase, int force,
|
|||||||
{
|
{
|
||||||
unsigned char *p;
|
unsigned char *p;
|
||||||
|
|
||||||
rc = agent_protect (buf, passphrase, &p, &len, s2k_count);
|
rc = agent_protect (buf, passphrase, &p, &len, s2k_count, -1);
|
||||||
if (rc)
|
if (rc)
|
||||||
{
|
{
|
||||||
xfree (buf);
|
xfree (buf);
|
||||||
|
@ -43,23 +43,6 @@ optional but required for some operations to calculate the fingerprint
|
|||||||
of the key. This timestamp should be a string with the number of
|
of the key. This timestamp should be a string with the number of
|
||||||
seconds since Epoch or an ISO time string (yyyymmddThhmmss).
|
seconds since Epoch or an ISO time string (yyyymmddThhmmss).
|
||||||
|
|
||||||
Actually this form should not be used for regular purposes and only
|
|
||||||
accepted by gpg-agent with the configuration option:
|
|
||||||
--allow-non-canonical-key-format. The regular way to represent the
|
|
||||||
keys is in canonical representation[3]:
|
|
||||||
|
|
||||||
(private-key
|
|
||||||
(rsa
|
|
||||||
(n #00e0ce9..[some bytes not shown]..51#)
|
|
||||||
(e #010001#)
|
|
||||||
(d #046129F..[some bytes not shown]..81#)
|
|
||||||
(p #00e861b..[some bytes not shown]..f1#)
|
|
||||||
(q #00f7a7c..[some bytes not shown]..61#)
|
|
||||||
(u #304559a..[some bytes not shown]..9b#)
|
|
||||||
)
|
|
||||||
(uri http://foo.bar x-foo:whatever_you_want)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
Protected Private Key Format
|
Protected Private Key Format
|
||||||
==============================
|
==============================
|
||||||
@ -90,7 +73,7 @@ The currently defined protection modes are:
|
|||||||
|
|
||||||
This describes an algorithm using using AES in CBC mode for
|
This describes an algorithm using using AES in CBC mode for
|
||||||
encryption, SHA-1 for integrity protection and the String to Key
|
encryption, SHA-1 for integrity protection and the String to Key
|
||||||
algorithm 3 from OpenPGP (rfc2440).
|
algorithm 3 from OpenPGP (rfc4880).
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@ -116,7 +99,7 @@ The currently defined protection modes are:
|
|||||||
easily be stripped by looking for the end of the list.
|
easily be stripped by looking for the end of the list.
|
||||||
|
|
||||||
The hash is calculated on the concatenation of the public key and
|
The hash is calculated on the concatenation of the public key and
|
||||||
secret key parameter lists: i.e it is required to hash the
|
secret key parameter lists: i.e. it is required to hash the
|
||||||
concatenation of these 6 canonical encoded lists for RSA, including
|
concatenation of these 6 canonical encoded lists for RSA, including
|
||||||
the parenthesis, the algorithm keyword and (if used) the protected-at
|
the parenthesis, the algorithm keyword and (if used) the protected-at
|
||||||
list.
|
list.
|
||||||
@ -135,7 +118,46 @@ The currently defined protection modes are:
|
|||||||
the stored one - If they don't match the integrity of the key is not
|
the stored one - If they don't match the integrity of the key is not
|
||||||
given.
|
given.
|
||||||
|
|
||||||
2. openpgp-native
|
2. 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
|
||||||
|
from OpenPGP (rfc4880).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
(protected openpgp-s2k3-ocb-aes
|
||||||
|
((sha1 16byte_salt no_of_iterations) 12byte_nonce)
|
||||||
|
encrypted_octet_string
|
||||||
|
)
|
||||||
|
|
||||||
|
The encrypted_octet string should yield this S-Exp (in canonical
|
||||||
|
representation) after decryption:
|
||||||
|
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(d #046129F..[some bytes not shown]..81#)
|
||||||
|
(p #00e861b..[some bytes not shown]..f1#)
|
||||||
|
(q #00f7a7c..[some bytes not shown]..61#)
|
||||||
|
(u #304559a..[some bytes not shown]..9b#)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
For padding reasons, random bytes may be appended to this list -
|
||||||
|
they can easily be stripped by looking for the end of the list.
|
||||||
|
|
||||||
|
The associated data required for this protection mode is the list
|
||||||
|
formiing the public key parameters. For the above example this is
|
||||||
|
is this canonical encoded S-expression:
|
||||||
|
|
||||||
|
(rsa
|
||||||
|
(n #00e0ce9..[some bytes not shown]..51#)
|
||||||
|
(e #010001#)
|
||||||
|
(protected-at "18950523T000000")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
3. openpgp-native
|
||||||
|
|
||||||
This is a wrapper around the OpenPGP Private Key Transport format
|
This is a wrapper around the OpenPGP Private Key Transport format
|
||||||
which resembles the standard OpenPGP format and allows the use of an
|
which resembles the standard OpenPGP format and allows the use of an
|
||||||
|
@ -69,6 +69,7 @@ enum cmd_and_opt_values
|
|||||||
oHomedir,
|
oHomedir,
|
||||||
oPrompt,
|
oPrompt,
|
||||||
oStatusMsg,
|
oStatusMsg,
|
||||||
|
oDebugUseOCB,
|
||||||
|
|
||||||
oAgentProgram
|
oAgentProgram
|
||||||
};
|
};
|
||||||
@ -96,6 +97,7 @@ static const char *opt_passphrase;
|
|||||||
static char *opt_prompt;
|
static char *opt_prompt;
|
||||||
static int opt_status_msg;
|
static int opt_status_msg;
|
||||||
static const char *opt_agent_program;
|
static const char *opt_agent_program;
|
||||||
|
static int opt_debug_use_ocb;
|
||||||
|
|
||||||
static char *get_passphrase (int promptno);
|
static char *get_passphrase (int promptno);
|
||||||
static void release_passphrase (char *pw);
|
static void release_passphrase (char *pw);
|
||||||
@ -132,6 +134,8 @@ static ARGPARSE_OPTS opts[] = {
|
|||||||
|
|
||||||
ARGPARSE_s_s (oAgentProgram, "agent-program", "@"),
|
ARGPARSE_s_s (oAgentProgram, "agent-program", "@"),
|
||||||
|
|
||||||
|
ARGPARSE_s_n (oDebugUseOCB, "debug-use-ocb", "@"), /* For hacking only. */
|
||||||
|
|
||||||
ARGPARSE_end ()
|
ARGPARSE_end ()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -333,7 +337,8 @@ read_and_protect (const char *fname)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
pw = get_passphrase (1);
|
pw = get_passphrase (1);
|
||||||
rc = agent_protect (key, pw, &result, &resultlen, 0);
|
rc = agent_protect (key, pw, &result, &resultlen, 0,
|
||||||
|
opt_debug_use_ocb? 1 : -1);
|
||||||
release_passphrase (pw);
|
release_passphrase (pw);
|
||||||
xfree (key);
|
xfree (key);
|
||||||
if (rc)
|
if (rc)
|
||||||
@ -598,6 +603,7 @@ main (int argc, char **argv )
|
|||||||
case oHaveCert: opt_have_cert = 1; break;
|
case oHaveCert: opt_have_cert = 1; break;
|
||||||
case oPrompt: opt_prompt = pargs.r.ret_str; break;
|
case oPrompt: opt_prompt = pargs.r.ret_str; break;
|
||||||
case oStatusMsg: opt_status_msg = 1; break;
|
case oStatusMsg: opt_status_msg = 1; break;
|
||||||
|
case oDebugUseOCB: opt_debug_use_ocb = 1; break;
|
||||||
|
|
||||||
default: pargs.err = ARGPARSE_PRINT_ERROR; break;
|
default: pargs.err = ARGPARSE_PRINT_ERROR; break;
|
||||||
}
|
}
|
||||||
|
432
agent/protect.c
432
agent/protect.c
@ -41,6 +41,18 @@
|
|||||||
#include "cvt-openpgp.h"
|
#include "cvt-openpgp.h"
|
||||||
#include "sexp-parse.h"
|
#include "sexp-parse.h"
|
||||||
|
|
||||||
|
|
||||||
|
#if GCRYPT_VERSION_NUMBER < 0x010700
|
||||||
|
# define OCB_MODE_SUPPORTED 0
|
||||||
|
#else
|
||||||
|
# define OCB_MODE_SUPPORTED 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* To use the openpgp-s2k3-ocb-aes scheme by default set the value of
|
||||||
|
* this macro to 1. Note that the caller of agent_protect may
|
||||||
|
* override this default. */
|
||||||
|
#define PROT_DEFAULT_TO_OCB 0
|
||||||
|
|
||||||
/* The protection mode for encryption. The supported modes for
|
/* The protection mode for encryption. The supported modes for
|
||||||
decryption are listed in agent_unprotect(). */
|
decryption are listed in agent_unprotect(). */
|
||||||
#define PROT_CIPHER GCRY_CIPHER_AES128
|
#define PROT_CIPHER GCRY_CIPHER_AES128
|
||||||
@ -87,6 +99,7 @@ hash_passphrase (const char *passphrase, int hashalgo,
|
|||||||
unsigned char *key, size_t keylen);
|
unsigned char *key, size_t keylen);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Get the process time and store it in DATA. */
|
/* Get the process time and store it in DATA. */
|
||||||
static void
|
static void
|
||||||
@ -302,70 +315,108 @@ calculate_mic (const unsigned char *plainkey, unsigned char *sha1hash)
|
|||||||
encrypted block in RESULT or return with an error code. SHA1HASH
|
encrypted block in RESULT or return with an error code. SHA1HASH
|
||||||
is the 20 byte SHA-1 hash required for the integrity code.
|
is the 20 byte SHA-1 hash required for the integrity code.
|
||||||
|
|
||||||
The parameter block is expected to be an incomplete S-Expression of
|
The parameter block is expected to be an incomplete canonical
|
||||||
the form (example in advanced format):
|
encoded S-Expression of the form (example in advanced format):
|
||||||
|
|
||||||
(d #046129F..[some bytes not shown]..81#)
|
(d #046129F..[some bytes not shown]..81#)
|
||||||
(p #00e861b..[some bytes not shown]..f1#)
|
(p #00e861b..[some bytes not shown]..f1#)
|
||||||
(q #00f7a7c..[some bytes not shown]..61#)
|
(q #00f7a7c..[some bytes not shown]..61#)
|
||||||
(u #304559a..[some bytes not shown]..9b#)
|
(u #304559a..[some bytes not shown]..9b#)
|
||||||
|
|
||||||
the returned block is the S-Expression:
|
the returned block is the S-Expression:
|
||||||
|
|
||||||
(protected mode (parms) encrypted_octet_string)
|
(protected mode (parms) encrypted_octet_string)
|
||||||
|
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
do_encryption (const unsigned char *protbegin, size_t protlen,
|
do_encryption (const unsigned char *hashbegin, size_t hashlen,
|
||||||
const char *passphrase, const unsigned char *sha1hash,
|
const unsigned char *protbegin, size_t protlen,
|
||||||
|
const char *passphrase,
|
||||||
|
const char *timestamp_exp, size_t timestamp_exp_len,
|
||||||
unsigned char **result, size_t *resultlen,
|
unsigned char **result, size_t *resultlen,
|
||||||
unsigned long s2k_count)
|
unsigned long s2k_count, int use_ocb)
|
||||||
{
|
{
|
||||||
gcry_cipher_hd_t hd;
|
gcry_cipher_hd_t hd;
|
||||||
const char *modestr = "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc";
|
const char *modestr;
|
||||||
|
unsigned char hashvalue[20];
|
||||||
int blklen, enclen, outlen;
|
int blklen, enclen, outlen;
|
||||||
unsigned char *iv = NULL;
|
unsigned char *iv = NULL;
|
||||||
|
unsigned int ivsize; /* Size of the buffer allocated for IV. */
|
||||||
|
const unsigned char *s2ksalt; /* Points into IV. */
|
||||||
int rc;
|
int rc;
|
||||||
char *outbuf = NULL;
|
char *outbuf = NULL;
|
||||||
char *p;
|
char *p;
|
||||||
int saltpos, ivpos, encpos;
|
int saltpos, ivpos, encpos;
|
||||||
|
|
||||||
|
s2ksalt = iv; /* Silence compiler warning. */
|
||||||
|
|
||||||
*resultlen = 0;
|
*resultlen = 0;
|
||||||
*result = NULL;
|
*result = NULL;
|
||||||
|
|
||||||
rc = gcry_cipher_open (&hd, PROT_CIPHER, GCRY_CIPHER_MODE_CBC,
|
if (use_ocb && !OCB_MODE_SUPPORTED)
|
||||||
|
return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION);
|
||||||
|
|
||||||
|
modestr = (use_ocb? "openpgp-s2k3-ocb-aes"
|
||||||
|
/* */: "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc");
|
||||||
|
|
||||||
|
rc = gcry_cipher_open (&hd, PROT_CIPHER,
|
||||||
|
#if OCB_MODE_SUPPORTED
|
||||||
|
use_ocb? GCRY_CIPHER_MODE_OCB :
|
||||||
|
#endif
|
||||||
|
GCRY_CIPHER_MODE_CBC,
|
||||||
GCRY_CIPHER_SECURE);
|
GCRY_CIPHER_SECURE);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
|
|
||||||
/* We need to work on a copy of the data because this makes it
|
/* We need to work on a copy of the data because this makes it
|
||||||
easier to add the trailer and the padding and more important we
|
* easier to add the trailer and the padding and more important we
|
||||||
have to prefix the text with 2 parenthesis, so we have to
|
* have to prefix the text with 2 parenthesis. In CBC mode we
|
||||||
allocate enough space for:
|
* have to allocate enough space for:
|
||||||
|
*
|
||||||
((<parameter_list>)(4:hash4:sha120:<hashvalue>)) + padding
|
* ((<parameter_list>)(4:hash4:sha120:<hashvalue>)) + padding
|
||||||
|
*
|
||||||
We always append a full block of random bytes as padding but
|
* we always append a full block of random bytes as padding but
|
||||||
encrypt only what is needed for a full blocksize. */
|
* encrypt only what is needed for a full blocksize. In OCB mode we
|
||||||
|
* have to allocate enough space for just:
|
||||||
|
*
|
||||||
|
* ((<parameter_list>))
|
||||||
|
*/
|
||||||
blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER);
|
blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER);
|
||||||
outlen = 2 + protlen + 2 + 6 + 6 + 23 + 2 + blklen;
|
if (use_ocb)
|
||||||
enclen = outlen/blklen * blklen;
|
{
|
||||||
outbuf = gcry_malloc_secure (outlen);
|
/* (( )) */
|
||||||
|
outlen = 2 + protlen + 2 ;
|
||||||
|
enclen = outlen + 16 /* taglen */;
|
||||||
|
outbuf = gcry_malloc_secure (enclen);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* (( )( 4:hash 4:sha1 20:<hash> )) <padding> */
|
||||||
|
outlen = 2 + protlen + 2 + 6 + 6 + 23 + 2 + blklen;
|
||||||
|
enclen = outlen/blklen * blklen;
|
||||||
|
outbuf = gcry_malloc_secure (outlen);
|
||||||
|
}
|
||||||
if (!outbuf)
|
if (!outbuf)
|
||||||
rc = out_of_core ();
|
rc = out_of_core ();
|
||||||
|
|
||||||
|
/* Allocate a buffer for the nonce and the salt. */
|
||||||
if (!rc)
|
if (!rc)
|
||||||
{
|
{
|
||||||
/* Allocate random bytes to be used as IV, padding and s2k salt. */
|
/* Allocate random bytes to be used as IV, padding and s2k salt
|
||||||
iv = xtrymalloc (blklen*2+8);
|
* or in OCB mode for a nonce and the s2k salt. The IV/nonce is
|
||||||
|
* set later because for OCB we need to set the key first. */
|
||||||
|
ivsize = (use_ocb? 12 : (blklen*2)) + 8;
|
||||||
|
iv = xtrymalloc (ivsize);
|
||||||
if (!iv)
|
if (!iv)
|
||||||
rc = gpg_error (GPG_ERR_ENOMEM);
|
rc = gpg_error_from_syserror ();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
gcry_create_nonce (iv, blklen*2+8);
|
gcry_create_nonce (iv, ivsize);
|
||||||
rc = gcry_cipher_setiv (hd, iv, blklen);
|
s2ksalt = iv + ivsize - 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Hash the passphrase and set the key. */
|
||||||
if (!rc)
|
if (!rc)
|
||||||
{
|
{
|
||||||
unsigned char *key;
|
unsigned char *key;
|
||||||
@ -377,14 +428,52 @@ do_encryption (const unsigned char *protbegin, size_t protlen,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
rc = hash_passphrase (passphrase, GCRY_MD_SHA1,
|
rc = hash_passphrase (passphrase, GCRY_MD_SHA1,
|
||||||
3, iv+2*blklen,
|
3, s2ksalt,
|
||||||
s2k_count ? s2k_count : get_standard_s2k_count(),
|
s2k_count? s2k_count:get_standard_s2k_count(),
|
||||||
key, keylen);
|
key, keylen);
|
||||||
if (!rc)
|
if (!rc)
|
||||||
rc = gcry_cipher_setkey (hd, key, keylen);
|
rc = gcry_cipher_setkey (hd, key, keylen);
|
||||||
xfree (key);
|
xfree (key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set the IV/nonce. */
|
||||||
|
if (!rc)
|
||||||
|
{
|
||||||
|
rc = gcry_cipher_setiv (hd, iv, use_ocb? 12 : blklen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (use_ocb)
|
||||||
|
{
|
||||||
|
/* In OCB Mode we use only the public key parameters as AAD. */
|
||||||
|
rc = gcry_cipher_authenticate (hd, hashbegin, protbegin - hashbegin);
|
||||||
|
if (!rc)
|
||||||
|
rc = gcry_cipher_authenticate (hd, timestamp_exp, timestamp_exp_len);
|
||||||
|
if (!rc)
|
||||||
|
rc = gcry_cipher_authenticate
|
||||||
|
(hd, protbegin+protlen, hashlen - (protbegin+protlen - hashbegin));
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Hash the entire expression for CBC mode. Because
|
||||||
|
* TIMESTAMP_EXP won't get protected, we can't simply hash a
|
||||||
|
* continuous buffer but need to call md_write several times. */
|
||||||
|
gcry_md_hd_t md;
|
||||||
|
|
||||||
|
rc = gcry_md_open (&md, GCRY_MD_SHA1, 0 );
|
||||||
|
if (!rc)
|
||||||
|
{
|
||||||
|
gcry_md_write (md, hashbegin, hashlen);
|
||||||
|
gcry_md_write (md, timestamp_exp, timestamp_exp_len);
|
||||||
|
gcry_md_write (md, ")", 1);
|
||||||
|
memcpy (hashvalue, gcry_md_read (md, GCRY_MD_SHA1), 20);
|
||||||
|
gcry_md_close (md);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Encrypt. */
|
||||||
if (!rc)
|
if (!rc)
|
||||||
{
|
{
|
||||||
p = outbuf;
|
p = outbuf;
|
||||||
@ -392,17 +481,42 @@ do_encryption (const unsigned char *protbegin, size_t protlen,
|
|||||||
*p++ = '(';
|
*p++ = '(';
|
||||||
memcpy (p, protbegin, protlen);
|
memcpy (p, protbegin, protlen);
|
||||||
p += protlen;
|
p += protlen;
|
||||||
memcpy (p, ")(4:hash4:sha120:", 17);
|
if (use_ocb)
|
||||||
p += 17;
|
{
|
||||||
memcpy (p, sha1hash, 20);
|
*p++ = ')';
|
||||||
p += 20;
|
*p++ = ')';
|
||||||
*p++ = ')';
|
}
|
||||||
*p++ = ')';
|
else
|
||||||
memcpy (p, iv+blklen, blklen);
|
{
|
||||||
p += blklen;
|
memcpy (p, ")(4:hash4:sha120:", 17);
|
||||||
|
p += 17;
|
||||||
|
memcpy (p, hashvalue, 20);
|
||||||
|
p += 20;
|
||||||
|
*p++ = ')';
|
||||||
|
*p++ = ')';
|
||||||
|
memcpy (p, iv+blklen, blklen); /* Add padding. */
|
||||||
|
p += blklen;
|
||||||
|
}
|
||||||
assert ( p - outbuf == outlen);
|
assert ( p - outbuf == outlen);
|
||||||
rc = gcry_cipher_encrypt (hd, outbuf, enclen, NULL, 0);
|
#if OCB_MODE_SUPPORTED
|
||||||
|
if (use_ocb)
|
||||||
|
{
|
||||||
|
gcry_cipher_final (hd);
|
||||||
|
rc = gcry_cipher_encrypt (hd, outbuf, outlen, NULL, 0);
|
||||||
|
if (!rc)
|
||||||
|
{
|
||||||
|
log_assert (outlen + 16 == enclen);
|
||||||
|
rc = gcry_cipher_gettag (hd, outbuf + outlen, 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif /*OCB_MODE_SUPPORTED*/
|
||||||
|
{
|
||||||
|
rc = gcry_cipher_encrypt (hd, outbuf, enclen, NULL, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Release cipher handle and check for errors. */
|
||||||
gcry_cipher_close (hd);
|
gcry_cipher_close (hd);
|
||||||
if (rc)
|
if (rc)
|
||||||
{
|
{
|
||||||
@ -429,7 +543,7 @@ do_encryption (const unsigned char *protbegin, size_t protlen,
|
|||||||
(int)strlen (modestr), modestr,
|
(int)strlen (modestr), modestr,
|
||||||
&saltpos,
|
&saltpos,
|
||||||
(unsigned int)strlen (countbuf), countbuf,
|
(unsigned int)strlen (countbuf), countbuf,
|
||||||
blklen, &ivpos, blklen, "",
|
use_ocb? 12 : blklen, &ivpos, use_ocb? 12 : blklen, "",
|
||||||
enclen, &encpos, enclen, "");
|
enclen, &encpos, enclen, "");
|
||||||
if (!p)
|
if (!p)
|
||||||
{
|
{
|
||||||
@ -438,11 +552,12 @@ do_encryption (const unsigned char *protbegin, size_t protlen,
|
|||||||
xfree (outbuf);
|
xfree (outbuf);
|
||||||
return tmperr;
|
return tmperr;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
*resultlen = strlen (p);
|
*resultlen = strlen (p);
|
||||||
*result = (unsigned char*)p;
|
*result = (unsigned char*)p;
|
||||||
memcpy (p+saltpos, iv+2*blklen, 8);
|
memcpy (p+saltpos, s2ksalt, 8);
|
||||||
memcpy (p+ivpos, iv, blklen);
|
memcpy (p+ivpos, iv, use_ocb? 12 : blklen);
|
||||||
memcpy (p+encpos, outbuf, enclen);
|
memcpy (p+encpos, outbuf, enclen);
|
||||||
xfree (iv);
|
xfree (iv);
|
||||||
xfree (outbuf);
|
xfree (outbuf);
|
||||||
@ -452,11 +567,13 @@ do_encryption (const unsigned char *protbegin, size_t protlen,
|
|||||||
|
|
||||||
|
|
||||||
/* Protect the key encoded in canonical format in PLAINKEY. We assume
|
/* Protect the key encoded in canonical format in PLAINKEY. We assume
|
||||||
a valid S-Exp here. */
|
a valid S-Exp here. With USE_UCB set to -1 the default scheme is
|
||||||
|
used (ie. either CBC or OCB), set to 0 the old CBC mode is used,
|
||||||
|
and set to 1 OCB is used. */
|
||||||
int
|
int
|
||||||
agent_protect (const unsigned char *plainkey, const char *passphrase,
|
agent_protect (const unsigned char *plainkey, const char *passphrase,
|
||||||
unsigned char **result, size_t *resultlen,
|
unsigned char **result, size_t *resultlen,
|
||||||
unsigned long s2k_count)
|
unsigned long s2k_count, int use_ocb)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
const char *parmlist;
|
const char *parmlist;
|
||||||
@ -466,15 +583,16 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
|
|||||||
const unsigned char *prot_begin, *prot_end, *real_end;
|
const unsigned char *prot_begin, *prot_end, *real_end;
|
||||||
size_t n;
|
size_t n;
|
||||||
int c, infidx, i;
|
int c, infidx, i;
|
||||||
unsigned char hashvalue[20];
|
|
||||||
char timestamp_exp[35];
|
char timestamp_exp[35];
|
||||||
unsigned char *protected;
|
unsigned char *protected;
|
||||||
size_t protectedlen;
|
size_t protectedlen;
|
||||||
int depth = 0;
|
int depth = 0;
|
||||||
unsigned char *p;
|
unsigned char *p;
|
||||||
gcry_md_hd_t md;
|
|
||||||
int have_curve = 0;
|
int have_curve = 0;
|
||||||
|
|
||||||
|
if (use_ocb == -1)
|
||||||
|
use_ocb = PROT_DEFAULT_TO_OCB;
|
||||||
|
|
||||||
/* Create an S-expression with the protected-at timestamp. */
|
/* Create an S-expression with the protected-at timestamp. */
|
||||||
memcpy (timestamp_exp, "(12:protected-at15:", 19);
|
memcpy (timestamp_exp, "(12:protected-at15:", 19);
|
||||||
gnupg_get_isotime (timestamp_exp+19);
|
gnupg_get_isotime (timestamp_exp+19);
|
||||||
@ -575,21 +693,10 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
|
|||||||
assert (!depth);
|
assert (!depth);
|
||||||
real_end = s-1;
|
real_end = s-1;
|
||||||
|
|
||||||
/* Hash the stuff. Because the timestamp_exp won't get protected,
|
rc = do_encryption (hash_begin, hash_end - hash_begin + 1,
|
||||||
we can't simply hash a continuous buffer but need to use several
|
prot_begin, prot_end - prot_begin + 1,
|
||||||
md_writes. */
|
passphrase, timestamp_exp, sizeof (timestamp_exp),
|
||||||
rc = gcry_md_open (&md, GCRY_MD_SHA1, 0 );
|
&protected, &protectedlen, s2k_count, use_ocb);
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
gcry_md_write (md, hash_begin, hash_end - hash_begin);
|
|
||||||
gcry_md_write (md, timestamp_exp, 35);
|
|
||||||
gcry_md_write (md, ")", 1);
|
|
||||||
memcpy (hashvalue, gcry_md_read (md, GCRY_MD_SHA1), 20);
|
|
||||||
gcry_md_close (md);
|
|
||||||
|
|
||||||
rc = do_encryption (prot_begin, prot_end - prot_begin + 1,
|
|
||||||
passphrase, hashvalue,
|
|
||||||
&protected, &protectedlen, s2k_count);
|
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
@ -631,11 +738,13 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
|
|||||||
|
|
||||||
/* Do the actual decryption and check the return list for consistency. */
|
/* Do the actual decryption and check the return list for consistency. */
|
||||||
static int
|
static int
|
||||||
do_decryption (const unsigned char *protected, size_t protectedlen,
|
do_decryption (const unsigned char *aad_begin, size_t aad_len,
|
||||||
|
const unsigned char *aadhole_begin, size_t aadhole_len,
|
||||||
|
const unsigned char *protected, size_t protectedlen,
|
||||||
const char *passphrase,
|
const char *passphrase,
|
||||||
const unsigned char *s2ksalt, unsigned long s2kcount,
|
const unsigned char *s2ksalt, unsigned long s2kcount,
|
||||||
const unsigned char *iv, size_t ivlen,
|
const unsigned char *iv, size_t ivlen,
|
||||||
int prot_cipher, int prot_cipher_keylen,
|
int prot_cipher, int prot_cipher_keylen, int is_ocb,
|
||||||
unsigned char **result)
|
unsigned char **result)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
@ -644,11 +753,29 @@ do_decryption (const unsigned char *protected, size_t protectedlen,
|
|||||||
unsigned char *outbuf;
|
unsigned char *outbuf;
|
||||||
size_t reallen;
|
size_t reallen;
|
||||||
|
|
||||||
blklen = gcry_cipher_get_algo_blklen (prot_cipher);
|
if (is_ocb && !OCB_MODE_SUPPORTED)
|
||||||
if (protectedlen < 4 || (protectedlen%blklen))
|
return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION);
|
||||||
return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
|
|
||||||
|
|
||||||
rc = gcry_cipher_open (&hd, prot_cipher, GCRY_CIPHER_MODE_CBC,
|
blklen = gcry_cipher_get_algo_blklen (prot_cipher);
|
||||||
|
if (is_ocb)
|
||||||
|
{
|
||||||
|
/* OCB does not require a multiple of the block length but we
|
||||||
|
* check that it is long enough for the 128 bit tag and that we
|
||||||
|
* have the 96 bit nonce. */
|
||||||
|
if (protectedlen < (4 + 16) || ivlen != 12)
|
||||||
|
return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (protectedlen < 4 || (protectedlen%blklen))
|
||||||
|
return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = gcry_cipher_open (&hd, prot_cipher,
|
||||||
|
#if OCB_MODE_SUPPORTED
|
||||||
|
is_ocb? GCRY_CIPHER_MODE_OCB :
|
||||||
|
#endif
|
||||||
|
GCRY_CIPHER_MODE_CBC,
|
||||||
GCRY_CIPHER_SECURE);
|
GCRY_CIPHER_SECURE);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
@ -656,8 +783,8 @@ do_decryption (const unsigned char *protected, size_t protectedlen,
|
|||||||
outbuf = gcry_malloc_secure (protectedlen);
|
outbuf = gcry_malloc_secure (protectedlen);
|
||||||
if (!outbuf)
|
if (!outbuf)
|
||||||
rc = out_of_core ();
|
rc = out_of_core ();
|
||||||
if (!rc)
|
|
||||||
rc = gcry_cipher_setiv (hd, iv, ivlen);
|
/* Hash the passphrase and set the key. */
|
||||||
if (!rc)
|
if (!rc)
|
||||||
{
|
{
|
||||||
unsigned char *key;
|
unsigned char *key;
|
||||||
@ -674,21 +801,60 @@ do_decryption (const unsigned char *protected, size_t protectedlen,
|
|||||||
xfree (key);
|
xfree (key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Set the IV/nonce. */
|
||||||
if (!rc)
|
if (!rc)
|
||||||
rc = gcry_cipher_decrypt (hd, outbuf, protectedlen,
|
{
|
||||||
protected, protectedlen);
|
rc = gcry_cipher_setiv (hd, iv, ivlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decrypt. */
|
||||||
|
if (!rc)
|
||||||
|
{
|
||||||
|
#if OCB_MODE_SUPPORTED
|
||||||
|
if (is_ocb)
|
||||||
|
{
|
||||||
|
rc = gcry_cipher_authenticate (hd, aad_begin,
|
||||||
|
aadhole_begin - aad_begin);
|
||||||
|
if (!rc)
|
||||||
|
rc = gcry_cipher_authenticate
|
||||||
|
(hd, aadhole_begin + aadhole_len,
|
||||||
|
aad_len - (aadhole_begin+aadhole_len - aad_begin));
|
||||||
|
|
||||||
|
if (!rc)
|
||||||
|
{
|
||||||
|
gcry_cipher_final (hd);
|
||||||
|
rc = gcry_cipher_decrypt (hd, outbuf, protectedlen - 16,
|
||||||
|
protected, protectedlen - 16);
|
||||||
|
}
|
||||||
|
if (!rc)
|
||||||
|
rc = gcry_cipher_checktag (hd, protected + protectedlen - 16, 16);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif /*OCB_MODE_SUPPORTED*/
|
||||||
|
{
|
||||||
|
rc = gcry_cipher_decrypt (hd, outbuf, protectedlen,
|
||||||
|
protected, protectedlen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Release cipher handle and check for errors. */
|
||||||
gcry_cipher_close (hd);
|
gcry_cipher_close (hd);
|
||||||
if (rc)
|
if (rc)
|
||||||
{
|
{
|
||||||
xfree (outbuf);
|
xfree (outbuf);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
/* Do a quick check first. */
|
|
||||||
|
/* Do a quick check on the data structure. */
|
||||||
if (*outbuf != '(' && outbuf[1] != '(')
|
if (*outbuf != '(' && outbuf[1] != '(')
|
||||||
{
|
{
|
||||||
|
/* Note that in OCB mode this is actually invalid _encrypted_
|
||||||
|
* data and not a bad passphrase. */
|
||||||
xfree (outbuf);
|
xfree (outbuf);
|
||||||
return gpg_error (GPG_ERR_BAD_PASSPHRASE);
|
return gpg_error (GPG_ERR_BAD_PASSPHRASE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check that we have a consistent S-Exp. */
|
/* Check that we have a consistent S-Exp. */
|
||||||
reallen = gcry_sexp_canon_len (outbuf, protectedlen, NULL, NULL);
|
reallen = gcry_sexp_canon_len (outbuf, protectedlen, NULL, NULL);
|
||||||
if (!reallen || (reallen + blklen < protectedlen) )
|
if (!reallen || (reallen + blklen < protectedlen) )
|
||||||
@ -702,11 +868,12 @@ do_decryption (const unsigned char *protected, size_t protectedlen,
|
|||||||
|
|
||||||
|
|
||||||
/* Merge the parameter list contained in CLEARTEXT with the original
|
/* Merge the parameter list contained in CLEARTEXT with the original
|
||||||
protect lists PROTECTEDKEY by replacing the list at REPLACEPOS.
|
* protect lists PROTECTEDKEY by replacing the list at REPLACEPOS.
|
||||||
Return the new list in RESULT and the MIC value in the 20 byte
|
* Return the new list in RESULT and the MIC value in the 20 byte
|
||||||
buffer SHA1HASH. CUTOFF and CUTLEN will receive the offset and the
|
* buffer SHA1HASH; if SHA1HASH is NULL no MIC will be computed.
|
||||||
length of the resulting list which should go into the MIC
|
* CUTOFF and CUTLEN will receive the offset and the length of the
|
||||||
calculation but then be removed. */
|
* resulting list which should go into the MIC calculation but then be
|
||||||
|
* removed. */
|
||||||
static int
|
static int
|
||||||
merge_lists (const unsigned char *protectedkey,
|
merge_lists (const unsigned char *protectedkey,
|
||||||
size_t replacepos,
|
size_t replacepos,
|
||||||
@ -730,7 +897,7 @@ merge_lists (const unsigned char *protectedkey,
|
|||||||
return gpg_error (GPG_ERR_BUG);
|
return gpg_error (GPG_ERR_BUG);
|
||||||
|
|
||||||
/* Estimate the required size of the resulting list. We have a large
|
/* Estimate the required size of the resulting list. We have a large
|
||||||
safety margin of >20 bytes (MIC hash from CLEARTEXT and the
|
safety margin of >20 bytes (FIXME: MIC hash from CLEARTEXT and the
|
||||||
removed "protected-" */
|
removed "protected-" */
|
||||||
newlistlen = gcry_sexp_canon_len (protectedkey, 0, NULL, NULL);
|
newlistlen = gcry_sexp_canon_len (protectedkey, 0, NULL, NULL);
|
||||||
if (!newlistlen)
|
if (!newlistlen)
|
||||||
@ -749,7 +916,7 @@ merge_lists (const unsigned char *protectedkey,
|
|||||||
memcpy (p, protectedkey+15+10, replacepos-15-10);
|
memcpy (p, protectedkey+15+10, replacepos-15-10);
|
||||||
p += replacepos-15-10;
|
p += replacepos-15-10;
|
||||||
|
|
||||||
/* copy the cleartext */
|
/* Copy the cleartext. */
|
||||||
s = cleartext;
|
s = cleartext;
|
||||||
if (*s != '(' && s[1] != '(')
|
if (*s != '(' && s[1] != '(')
|
||||||
return gpg_error (GPG_ERR_BUG); /*we already checked this */
|
return gpg_error (GPG_ERR_BUG); /*we already checked this */
|
||||||
@ -774,26 +941,29 @@ merge_lists (const unsigned char *protectedkey,
|
|||||||
goto invalid_sexp;
|
goto invalid_sexp;
|
||||||
endpos = s;
|
endpos = s;
|
||||||
s++;
|
s++;
|
||||||
/* Intermezzo: Get the MIC */
|
|
||||||
if (*s != '(')
|
|
||||||
goto invalid_sexp;
|
|
||||||
s++;
|
|
||||||
n = snext (&s);
|
|
||||||
if (!smatch (&s, n, "hash"))
|
|
||||||
goto invalid_sexp;
|
|
||||||
n = snext (&s);
|
|
||||||
if (!smatch (&s, n, "sha1"))
|
|
||||||
goto invalid_sexp;
|
|
||||||
n = snext (&s);
|
|
||||||
if (n != 20)
|
|
||||||
goto invalid_sexp;
|
|
||||||
memcpy (sha1hash, s, 20);
|
|
||||||
s += n;
|
|
||||||
if (*s != ')')
|
|
||||||
goto invalid_sexp;
|
|
||||||
/* End intermezzo */
|
|
||||||
|
|
||||||
/* append the parameter list */
|
/* Intermezzo: Get the MIC if requested. */
|
||||||
|
if (sha1hash)
|
||||||
|
{
|
||||||
|
if (*s != '(')
|
||||||
|
goto invalid_sexp;
|
||||||
|
s++;
|
||||||
|
n = snext (&s);
|
||||||
|
if (!smatch (&s, n, "hash"))
|
||||||
|
goto invalid_sexp;
|
||||||
|
n = snext (&s);
|
||||||
|
if (!smatch (&s, n, "sha1"))
|
||||||
|
goto invalid_sexp;
|
||||||
|
n = snext (&s);
|
||||||
|
if (n != 20)
|
||||||
|
goto invalid_sexp;
|
||||||
|
memcpy (sha1hash, s, 20);
|
||||||
|
s += n;
|
||||||
|
if (*s != ')')
|
||||||
|
goto invalid_sexp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Append the parameter list. */
|
||||||
memcpy (p, startpos, endpos - startpos);
|
memcpy (p, startpos, endpos - startpos);
|
||||||
p += endpos - startpos;
|
p += endpos - startpos;
|
||||||
|
|
||||||
@ -867,9 +1037,11 @@ agent_unprotect (ctrl_t ctrl,
|
|||||||
const char *name; /* Name of the protection method. */
|
const char *name; /* Name of the protection method. */
|
||||||
int algo; /* (A zero indicates the "openpgp-native" hack.) */
|
int algo; /* (A zero indicates the "openpgp-native" hack.) */
|
||||||
int keylen; /* Used key length in bytes. */
|
int keylen; /* Used key length in bytes. */
|
||||||
|
unsigned int is_ocb:1;
|
||||||
} algotable[] = {
|
} algotable[] = {
|
||||||
{ "openpgp-s2k3-sha1-aes-cbc", GCRY_CIPHER_AES128, (128/8)},
|
{ "openpgp-s2k3-sha1-aes-cbc", GCRY_CIPHER_AES128, (128/8)},
|
||||||
{ "openpgp-s2k3-sha1-aes256-cbc", GCRY_CIPHER_AES256, (256/8)},
|
{ "openpgp-s2k3-sha1-aes256-cbc", GCRY_CIPHER_AES256, (256/8)},
|
||||||
|
{ "openpgp-s2k3-ocb-aes", GCRY_CIPHER_AES128, (128/8), 1},
|
||||||
{ "openpgp-native", 0, 0 }
|
{ "openpgp-native", 0, 0 }
|
||||||
};
|
};
|
||||||
int rc;
|
int rc;
|
||||||
@ -882,6 +1054,8 @@ agent_unprotect (ctrl_t ctrl,
|
|||||||
unsigned long s2kcount;
|
unsigned long s2kcount;
|
||||||
const unsigned char *iv;
|
const unsigned char *iv;
|
||||||
int prot_cipher, prot_cipher_keylen;
|
int prot_cipher, prot_cipher_keylen;
|
||||||
|
int is_ocb;
|
||||||
|
const unsigned char *aad_begin, *aad_end, *aadhole_begin, *aadhole_end;
|
||||||
const unsigned char *prot_begin;
|
const unsigned char *prot_begin;
|
||||||
unsigned char *cleartext;
|
unsigned char *cleartext;
|
||||||
unsigned char *final;
|
unsigned char *final;
|
||||||
@ -902,6 +1076,15 @@ agent_unprotect (ctrl_t ctrl,
|
|||||||
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
|
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
|
||||||
if (*s != '(')
|
if (*s != '(')
|
||||||
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
|
return gpg_error (GPG_ERR_UNKNOWN_SEXP);
|
||||||
|
{
|
||||||
|
aad_begin = aad_end = s;
|
||||||
|
aad_end++;
|
||||||
|
i = 1;
|
||||||
|
rc = sskip (&aad_end, &i);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
s++;
|
s++;
|
||||||
n = snext (&s);
|
n = snext (&s);
|
||||||
if (!n)
|
if (!n)
|
||||||
@ -913,7 +1096,6 @@ agent_unprotect (ctrl_t ctrl,
|
|||||||
if (!protect_info[infidx].algo)
|
if (!protect_info[infidx].algo)
|
||||||
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
|
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
|
||||||
|
|
||||||
|
|
||||||
/* See wether we have a protected-at timestamp. */
|
/* See wether we have a protected-at timestamp. */
|
||||||
protect_list = s; /* Save for later. */
|
protect_list = s; /* Save for later. */
|
||||||
if (protected_at)
|
if (protected_at)
|
||||||
@ -969,6 +1151,14 @@ agent_unprotect (ctrl_t ctrl,
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
/* found */
|
/* found */
|
||||||
|
{
|
||||||
|
aadhole_begin = aadhole_end = prot_begin;
|
||||||
|
aadhole_end++;
|
||||||
|
i = 1;
|
||||||
|
rc = sskip (&aadhole_end, &i);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
n = snext (&s);
|
n = snext (&s);
|
||||||
if (!n)
|
if (!n)
|
||||||
return gpg_error (GPG_ERR_INV_SEXP);
|
return gpg_error (GPG_ERR_INV_SEXP);
|
||||||
@ -976,14 +1166,17 @@ agent_unprotect (ctrl_t ctrl,
|
|||||||
/* Lookup the protection algo. */
|
/* Lookup the protection algo. */
|
||||||
prot_cipher = 0; /* (avoid gcc warning) */
|
prot_cipher = 0; /* (avoid gcc warning) */
|
||||||
prot_cipher_keylen = 0; /* (avoid gcc warning) */
|
prot_cipher_keylen = 0; /* (avoid gcc warning) */
|
||||||
for (i= 0; i < DIM (algotable); i++)
|
is_ocb = 0;
|
||||||
|
for (i=0; i < DIM (algotable); i++)
|
||||||
if (smatch (&s, n, algotable[i].name))
|
if (smatch (&s, n, algotable[i].name))
|
||||||
{
|
{
|
||||||
prot_cipher = algotable[i].algo;
|
prot_cipher = algotable[i].algo;
|
||||||
prot_cipher_keylen = algotable[i].keylen;
|
prot_cipher_keylen = algotable[i].keylen;
|
||||||
|
is_ocb = algotable[i].is_ocb;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (i == DIM (algotable))
|
if (i == DIM (algotable)
|
||||||
|
|| (is_ocb && !OCB_MODE_SUPPORTED))
|
||||||
return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION);
|
return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION);
|
||||||
|
|
||||||
if (!prot_cipher) /* This is "openpgp-native". */
|
if (!prot_cipher) /* This is "openpgp-native". */
|
||||||
@ -1048,8 +1241,16 @@ agent_unprotect (ctrl_t ctrl,
|
|||||||
s++; /* skip list end */
|
s++; /* skip list end */
|
||||||
|
|
||||||
n = snext (&s);
|
n = snext (&s);
|
||||||
if (n != 16) /* Wrong blocksize for IV (we support only 128 bit). */
|
if (is_ocb)
|
||||||
return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
|
{
|
||||||
|
if (n != 12) /* Wrong size of the nonce. */
|
||||||
|
return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (n != 16) /* Wrong blocksize for IV (we support only 128 bit). */
|
||||||
|
return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
|
||||||
|
}
|
||||||
iv = s;
|
iv = s;
|
||||||
s += n;
|
s += n;
|
||||||
if (*s != ')' )
|
if (*s != ')' )
|
||||||
@ -1060,15 +1261,19 @@ agent_unprotect (ctrl_t ctrl,
|
|||||||
return gpg_error (GPG_ERR_INV_SEXP);
|
return gpg_error (GPG_ERR_INV_SEXP);
|
||||||
|
|
||||||
cleartext = NULL; /* Avoid cc warning. */
|
cleartext = NULL; /* Avoid cc warning. */
|
||||||
rc = do_decryption (s, n,
|
rc = do_decryption (aad_begin, aad_end - aad_begin,
|
||||||
|
aadhole_begin, aadhole_end - aadhole_begin,
|
||||||
|
s, n,
|
||||||
passphrase, s2ksalt, s2kcount,
|
passphrase, s2ksalt, s2kcount,
|
||||||
iv, 16, prot_cipher, prot_cipher_keylen,
|
iv, is_ocb? 12:16,
|
||||||
|
prot_cipher, prot_cipher_keylen, is_ocb,
|
||||||
&cleartext);
|
&cleartext);
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
rc = merge_lists (protectedkey, prot_begin-protectedkey, cleartext,
|
rc = merge_lists (protectedkey, prot_begin-protectedkey, cleartext,
|
||||||
sha1hash, &final, &finallen, &cutoff, &cutlen);
|
is_ocb? NULL : sha1hash,
|
||||||
|
&final, &finallen, &cutoff, &cutlen);
|
||||||
/* Albeit cleartext has been allocated in secure memory and thus
|
/* Albeit cleartext has been allocated in secure memory and thus
|
||||||
xfree will wipe it out, we do an extra wipe just in case
|
xfree will wipe it out, we do an extra wipe just in case
|
||||||
somethings goes badly wrong. */
|
somethings goes badly wrong. */
|
||||||
@ -1077,15 +1282,19 @@ agent_unprotect (ctrl_t ctrl,
|
|||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
rc = calculate_mic (final, sha1hash2);
|
if (!is_ocb)
|
||||||
if (!rc && memcmp (sha1hash, sha1hash2, 20))
|
|
||||||
rc = gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
|
|
||||||
if (rc)
|
|
||||||
{
|
{
|
||||||
wipememory (final, finallen);
|
rc = calculate_mic (final, sha1hash2);
|
||||||
xfree (final);
|
if (!rc && memcmp (sha1hash, sha1hash2, 20))
|
||||||
return rc;
|
rc = gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
|
||||||
|
if (rc)
|
||||||
|
{
|
||||||
|
wipememory (final, finallen);
|
||||||
|
xfree (final);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now remove the part which is included in the MIC but should not
|
/* Now remove the part which is included in the MIC but should not
|
||||||
go into the final thing. */
|
go into the final thing. */
|
||||||
if (cutlen)
|
if (cutlen)
|
||||||
@ -1184,7 +1393,6 @@ agent_private_key_type (const unsigned char *privatekey)
|
|||||||
n = snext (&s);
|
n = snext (&s);
|
||||||
if (!n)
|
if (!n)
|
||||||
return PRIVATE_KEY_UNKNOWN; /* Invalid sexp. */
|
return PRIVATE_KEY_UNKNOWN; /* Invalid sexp. */
|
||||||
log_debug ("openpgp-native protection '%.*s'\n", (int)n, s);
|
|
||||||
if (smatch (&s, n, "none"))
|
if (smatch (&s, n, "none"))
|
||||||
return PRIVATE_KEY_OPENPGP_NONE; /* Yes. */
|
return PRIVATE_KEY_OPENPGP_NONE; /* Yes. */
|
||||||
}
|
}
|
||||||
|
@ -195,7 +195,7 @@ test_agent_protect (void)
|
|||||||
{
|
{
|
||||||
ret = agent_protect ((const unsigned char*)specs[i].key,
|
ret = agent_protect ((const unsigned char*)specs[i].key,
|
||||||
specs[i].passphrase,
|
specs[i].passphrase,
|
||||||
&specs[i].result, &specs[i].resultlen, 0);
|
&specs[i].result, &specs[i].resultlen, 0, -1);
|
||||||
if (gpg_err_code (ret) != specs[i].ret_expected)
|
if (gpg_err_code (ret) != specs[i].ret_expected)
|
||||||
{
|
{
|
||||||
printf ("agent_protect(%d) returned '%i/%s'; expected '%i/%s'\n",
|
printf ("agent_protect(%d) returned '%i/%s'; expected '%i/%s'\n",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user