mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
3a18378a92
* g10/free-packet.c (copy_public_key): Factor some code out to ... (copy_public_key_basics): new. * g10/build-packet.c (build_sig_subpkt_from_sig): New arg signhints. * g10/packet.h (PUBKEY_USAGE_RENC): Fix value. (SIGNHINT_KEYSIG, SIGNHINT_SELFSIG): Moved from sign.c. (SIGNHINT_ADSK): New. (PKT_public_key): Change pubkey_usage from byte to u16. (PKT_user_id): Cosmetic fix: change help_key_usage from int to u16. * g10/getkey.c (parse_key_usage): Make public. * g10/misc.c (openpgp_pk_algo_usage): Take PUBKEY_USAGE_RENC in account. * g10/sign.c (update_keysig_packet): Set SIGNHINT_ADSK. (make_keysig_packet): Ditto. (do_sign): No time warp check in ADSK mode. * g10/sig-check.c (check_signature_metadata_validity): Ditto. * g10/keygen.c (struct opaque_data_usage_and_pk): Remove. (write_keybinding): Do not use the removed struct. (do_add_key_flags): Support PUBKEY_USAGE_RENC and others. (keygen_add_key_flags_and_expire): Rewrite and make public. * g10/keyedit.c (enum cmdids): Add cmdADDADSK. (keyedit_menu): Add command "addadsk". (menu_addadsk): New. -- This makes use of a new encryption flag: The "restricted encryption key" (2nd,0x04) does not take part in any automatic selection of encryption keys. It is only found on a subkey signature (type 0x18), one that refers to the key the flag applies to. Followup patches will add encryption support and a --quick command. GnuPG-bug-id: 6395
2079 lines
56 KiB
C
2079 lines
56 KiB
C
/* build-packet.c - assemble packets and write them
|
|
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
|
|
* 2006, 2010, 2011 Free Software Foundation, Inc.
|
|
*
|
|
* 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include "gpg.h"
|
|
#include "../common/util.h"
|
|
#include "packet.h"
|
|
#include "../common/status.h"
|
|
#include "../common/iobuf.h"
|
|
#include "../common/i18n.h"
|
|
#include "options.h"
|
|
#include "../common/host2net.h"
|
|
|
|
static gpg_error_t do_ring_trust (iobuf_t out, PKT_ring_trust *rt);
|
|
static int do_user_id( IOBUF out, int ctb, PKT_user_id *uid );
|
|
static int do_key (iobuf_t out, int ctb, PKT_public_key *pk);
|
|
static int do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc );
|
|
static int do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc );
|
|
static u32 calc_plaintext( PKT_plaintext *pt );
|
|
static int do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt );
|
|
static int do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed );
|
|
static int do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed );
|
|
static int do_encrypted_aead (iobuf_t out, int ctb, PKT_encrypted *ed);
|
|
static int do_compressed( IOBUF out, int ctb, PKT_compressed *cd );
|
|
static int do_signature( IOBUF out, int ctb, PKT_signature *sig );
|
|
static int do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops );
|
|
|
|
static int calc_header_length( u32 len, int new_ctb );
|
|
static int write_16(IOBUF inp, u16 a);
|
|
static int write_32(IOBUF inp, u32 a);
|
|
static int write_header( IOBUF out, int ctb, u32 len );
|
|
static int write_sign_packet_header( IOBUF out, int ctb, u32 len );
|
|
static int write_header2( IOBUF out, int ctb, u32 len, int hdrlen );
|
|
static int write_new_header( IOBUF out, int ctb, u32 len, int hdrlen );
|
|
|
|
/* Returns 1 if CTB is a new format ctb and 0 if CTB is an old format
|
|
ctb. */
|
|
static int
|
|
ctb_new_format_p (int ctb)
|
|
{
|
|
/* Bit 7 must always be set. */
|
|
log_assert ((ctb & (1 << 7)));
|
|
/* Bit 6 indicates whether the packet is a new format packet. */
|
|
return (ctb & (1 << 6));
|
|
}
|
|
|
|
/* Extract the packet type from a CTB. */
|
|
static int
|
|
ctb_pkttype (int ctb)
|
|
{
|
|
if (ctb_new_format_p (ctb))
|
|
/* Bits 0 through 5 are the packet type. */
|
|
return (ctb & ((1 << 6) - 1));
|
|
else
|
|
/* Bits 2 through 5 are the packet type. */
|
|
return (ctb & ((1 << 6) - 1)) >> 2;
|
|
}
|
|
|
|
|
|
/* Build a keyblock image from KEYBLOCK. Returns 0 on success and
|
|
* only then stores a new iobuf object at R_IOBUF; the returned iobuf
|
|
* can be access with the iobuf_get_temp_buffer and
|
|
* iobuf_get_temp_length macros. */
|
|
gpg_error_t
|
|
build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf)
|
|
{
|
|
gpg_error_t err;
|
|
iobuf_t iobuf;
|
|
kbnode_t kbctx, node;
|
|
|
|
*r_iobuf = NULL;
|
|
|
|
iobuf = iobuf_temp ();
|
|
for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));)
|
|
{
|
|
/* Make sure to use only packets valid on a keyblock. */
|
|
switch (node->pkt->pkttype)
|
|
{
|
|
case PKT_PUBLIC_KEY:
|
|
case PKT_PUBLIC_SUBKEY:
|
|
case PKT_SIGNATURE:
|
|
case PKT_USER_ID:
|
|
case PKT_ATTRIBUTE:
|
|
case PKT_RING_TRUST:
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
err = build_packet_and_meta (iobuf, node->pkt);
|
|
if (err)
|
|
{
|
|
iobuf_close (iobuf);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
*r_iobuf = iobuf;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Build a packet and write it to the stream OUT.
|
|
* Returns: 0 on success or on an error code. */
|
|
int
|
|
build_packet (IOBUF out, PACKET *pkt)
|
|
{
|
|
int rc = 0;
|
|
int new_ctb = 0;
|
|
int ctb, pkttype;
|
|
|
|
if (DBG_PACKET)
|
|
log_debug ("build_packet() type=%d\n", pkt->pkttype);
|
|
log_assert (pkt->pkt.generic);
|
|
|
|
switch ((pkttype = pkt->pkttype))
|
|
{
|
|
case PKT_PUBLIC_KEY:
|
|
if (pkt->pkt.public_key->seckey_info)
|
|
pkttype = PKT_SECRET_KEY;
|
|
break;
|
|
case PKT_PUBLIC_SUBKEY:
|
|
if (pkt->pkt.public_key->seckey_info)
|
|
pkttype = PKT_SECRET_SUBKEY;
|
|
break;
|
|
case PKT_PLAINTEXT:
|
|
new_ctb = pkt->pkt.plaintext->new_ctb;
|
|
break;
|
|
case PKT_ENCRYPTED:
|
|
case PKT_ENCRYPTED_MDC:
|
|
case PKT_ENCRYPTED_AEAD:
|
|
new_ctb = pkt->pkt.encrypted->new_ctb;
|
|
break;
|
|
case PKT_COMPRESSED:
|
|
new_ctb = pkt->pkt.compressed->new_ctb;
|
|
break;
|
|
case PKT_USER_ID:
|
|
if (pkt->pkt.user_id->attrib_data)
|
|
pkttype = PKT_ATTRIBUTE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (new_ctb || pkttype > 15) /* new format */
|
|
ctb = (0xc0 | (pkttype & 0x3f));
|
|
else
|
|
ctb = (0x80 | ((pkttype & 15)<<2));
|
|
switch (pkttype)
|
|
{
|
|
case PKT_ATTRIBUTE:
|
|
case PKT_USER_ID:
|
|
rc = do_user_id (out, ctb, pkt->pkt.user_id);
|
|
break;
|
|
case PKT_OLD_COMMENT:
|
|
case PKT_COMMENT:
|
|
/* Ignore these. Theoretically, this will never be called as we
|
|
* have no way to output comment packets any longer, but just in
|
|
* case there is some code path that would end up outputting a
|
|
* comment that was written before comments were dropped (in the
|
|
* public key?) this is a no-op. */
|
|
break;
|
|
case PKT_PUBLIC_SUBKEY:
|
|
case PKT_PUBLIC_KEY:
|
|
case PKT_SECRET_SUBKEY:
|
|
case PKT_SECRET_KEY:
|
|
rc = do_key (out, ctb, pkt->pkt.public_key);
|
|
break;
|
|
case PKT_SYMKEY_ENC:
|
|
rc = do_symkey_enc (out, ctb, pkt->pkt.symkey_enc);
|
|
break;
|
|
case PKT_PUBKEY_ENC:
|
|
rc = do_pubkey_enc (out, ctb, pkt->pkt.pubkey_enc);
|
|
break;
|
|
case PKT_PLAINTEXT:
|
|
rc = do_plaintext (out, ctb, pkt->pkt.plaintext);
|
|
break;
|
|
case PKT_ENCRYPTED:
|
|
rc = do_encrypted (out, ctb, pkt->pkt.encrypted);
|
|
break;
|
|
case PKT_ENCRYPTED_MDC:
|
|
rc = do_encrypted_mdc (out, ctb, pkt->pkt.encrypted);
|
|
break;
|
|
case PKT_ENCRYPTED_AEAD:
|
|
rc = do_encrypted_aead (out, ctb, pkt->pkt.encrypted);
|
|
break;
|
|
case PKT_COMPRESSED:
|
|
rc = do_compressed (out, ctb, pkt->pkt.compressed);
|
|
break;
|
|
case PKT_SIGNATURE:
|
|
rc = do_signature (out, ctb, pkt->pkt.signature);
|
|
break;
|
|
case PKT_ONEPASS_SIG:
|
|
rc = do_onepass_sig (out, ctb, pkt->pkt.onepass_sig);
|
|
break;
|
|
case PKT_RING_TRUST:
|
|
/* Ignore it (only written by build_packet_and_meta) */
|
|
break;
|
|
case PKT_MDC:
|
|
/* We write it directly, so we should never see it here. */
|
|
default:
|
|
log_bug ("invalid packet type in build_packet()\n");
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/* Build a packet and write it to the stream OUT. This variant also
|
|
* writes the meta data using ring trust packets. Returns: 0 on
|
|
* success or on error code. */
|
|
gpg_error_t
|
|
build_packet_and_meta (iobuf_t out, PACKET *pkt)
|
|
{
|
|
gpg_error_t err;
|
|
PKT_ring_trust rt = {0};
|
|
|
|
err = build_packet (out, pkt);
|
|
if (err)
|
|
;
|
|
else if (pkt->pkttype == PKT_SIGNATURE)
|
|
{
|
|
PKT_signature *sig = pkt->pkt.signature;
|
|
|
|
rt.subtype = RING_TRUST_SIG;
|
|
/* Note: trustval is not yet used. */
|
|
if (sig->flags.checked)
|
|
{
|
|
rt.sigcache = 1;
|
|
if (sig->flags.valid)
|
|
rt.sigcache |= 2;
|
|
}
|
|
err = do_ring_trust (out, &rt);
|
|
}
|
|
else if (pkt->pkttype == PKT_USER_ID
|
|
|| pkt->pkttype == PKT_ATTRIBUTE)
|
|
{
|
|
PKT_user_id *uid = pkt->pkt.user_id;
|
|
|
|
rt.subtype = RING_TRUST_UID;
|
|
rt.keyorg = uid->keyorg;
|
|
rt.keyupdate = uid->keyupdate;
|
|
rt.url = uid->updateurl;
|
|
err = do_ring_trust (out, &rt);
|
|
rt.url = NULL;
|
|
}
|
|
else if (pkt->pkttype == PKT_PUBLIC_KEY
|
|
|| pkt->pkttype == PKT_SECRET_KEY)
|
|
{
|
|
PKT_public_key *pk = pkt->pkt.public_key;
|
|
|
|
rt.subtype = RING_TRUST_KEY;
|
|
rt.keyorg = pk->keyorg;
|
|
rt.keyupdate = pk->keyupdate;
|
|
rt.url = pk->updateurl;
|
|
err = do_ring_trust (out, &rt);
|
|
rt.url = NULL;
|
|
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
/*
|
|
* Write the mpi A to OUT. If R_NWRITTEN is not NULL the number of
|
|
* bytes written is stored there. To only get the number of bytes
|
|
* which would be written NULL may be passed for OUT.
|
|
*/
|
|
gpg_error_t
|
|
gpg_mpi_write (iobuf_t out, gcry_mpi_t a, unsigned int *r_nwritten)
|
|
{
|
|
gpg_error_t err;
|
|
unsigned int nwritten = 0;
|
|
|
|
if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
|
|
{
|
|
unsigned int nbits;
|
|
const unsigned char *p;
|
|
unsigned char lenhdr[2];
|
|
|
|
/* gcry_log_debugmpi ("a", a); */
|
|
p = gcry_mpi_get_opaque (a, &nbits);
|
|
if (p)
|
|
{
|
|
/* Strip leading zero bits. */
|
|
for (; nbits >= 8 && !*p; p++, nbits -= 8)
|
|
;
|
|
if (nbits >= 8 && !(*p & 0x80))
|
|
if (--nbits >= 7 && !(*p & 0x40))
|
|
if (--nbits >= 6 && !(*p & 0x20))
|
|
if (--nbits >= 5 && !(*p & 0x10))
|
|
if (--nbits >= 4 && !(*p & 0x08))
|
|
if (--nbits >= 3 && !(*p & 0x04))
|
|
if (--nbits >= 2 && !(*p & 0x02))
|
|
if (--nbits >= 1 && !(*p & 0x01))
|
|
--nbits;
|
|
}
|
|
/* gcry_log_debug (" [%u bit]\n", nbits); */
|
|
/* gcry_log_debughex (" ", p, (nbits+7)/8); */
|
|
lenhdr[0] = nbits >> 8;
|
|
lenhdr[1] = nbits;
|
|
err = out? iobuf_write (out, lenhdr, 2) : 0;
|
|
if (!err)
|
|
{
|
|
nwritten += 2;
|
|
if (p)
|
|
{
|
|
err = out? iobuf_write (out, p, (nbits+7)/8) : 0;
|
|
if (!err)
|
|
nwritten += (nbits+7)/8;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char buffer[(MAX_EXTERN_MPI_BITS+7)/8+2]; /* 2 is for the mpi length. */
|
|
size_t nbytes;
|
|
|
|
nbytes = DIM(buffer);
|
|
err = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, &nbytes, a );
|
|
if (!err)
|
|
{
|
|
err = out? iobuf_write (out, buffer, nbytes) : 0;
|
|
if (!err)
|
|
nwritten += nbytes;
|
|
}
|
|
else if (gpg_err_code (err) == GPG_ERR_TOO_SHORT )
|
|
{
|
|
log_info ("mpi too large (%u bits)\n", gcry_mpi_get_nbits (a));
|
|
/* The buffer was too small. We better tell the user about
|
|
* the MPI. */
|
|
err = gpg_error (GPG_ERR_TOO_LARGE);
|
|
}
|
|
}
|
|
|
|
if (r_nwritten)
|
|
*r_nwritten = nwritten;
|
|
return err;
|
|
}
|
|
|
|
|
|
/*
|
|
* Write the mpi A to the output stream OUT as "SOS" (Strange Octet
|
|
* String). If R_NWRITTEN is not NULL the number of bytes written is
|
|
* stored there. To only get the number of bytes which would be
|
|
* written, NULL may be passed for OUT.
|
|
*/
|
|
static gpg_error_t
|
|
sos_write (iobuf_t out, gcry_mpi_t a, unsigned int *r_nwritten)
|
|
{
|
|
gpg_error_t err;
|
|
unsigned int nwritten = 0;
|
|
|
|
if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
|
|
{
|
|
unsigned int nbits;
|
|
const unsigned char *p;
|
|
unsigned char lenhdr[2];
|
|
|
|
/* gcry_log_debugmpi ("a", a); */
|
|
p = gcry_mpi_get_opaque (a, &nbits);
|
|
/* gcry_log_debug (" [%u bit]\n", nbits); */
|
|
/* gcry_log_debughex (" ", p, (nbits+7)/8); */
|
|
|
|
if (p && *p)
|
|
{
|
|
nbits = ((nbits + 7) / 8) * 8;
|
|
|
|
if (nbits >= 8 && !(*p & 0x80))
|
|
if (--nbits >= 7 && !(*p & 0x40))
|
|
if (--nbits >= 6 && !(*p & 0x20))
|
|
if (--nbits >= 5 && !(*p & 0x10))
|
|
if (--nbits >= 4 && !(*p & 0x08))
|
|
if (--nbits >= 3 && !(*p & 0x04))
|
|
if (--nbits >= 2 && !(*p & 0x02))
|
|
if (--nbits >= 1 && !(*p & 0x01))
|
|
--nbits;
|
|
}
|
|
|
|
lenhdr[0] = nbits >> 8;
|
|
lenhdr[1] = nbits;
|
|
err = out? iobuf_write (out, lenhdr, 2) : 0;
|
|
if (!err)
|
|
{
|
|
nwritten += 2;
|
|
if (p)
|
|
{
|
|
err = out? iobuf_write (out, p, (nbits+7)/8) : 0;
|
|
if (!err)
|
|
nwritten += (nbits+7)/8;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
log_info ("non-opaque MPI (%u bits) for SOS\n", gcry_mpi_get_nbits (a));
|
|
err = gpg_error (GPG_ERR_INV_DATA);
|
|
}
|
|
|
|
if (r_nwritten)
|
|
*r_nwritten = nwritten;
|
|
return err;
|
|
}
|
|
|
|
|
|
/*
|
|
* Write an opaque string to the output stream without length info.
|
|
*/
|
|
gpg_error_t
|
|
gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a)
|
|
{
|
|
int rc;
|
|
|
|
if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
|
|
{
|
|
unsigned int nbits;
|
|
const void *p;
|
|
|
|
p = gcry_mpi_get_opaque (a, &nbits);
|
|
rc = p ? iobuf_write (out, p, (nbits+7)/8) : 0;
|
|
}
|
|
else
|
|
rc = gpg_error (GPG_ERR_BAD_MPI);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/* Calculate the length of a packet described by PKT. */
|
|
u32
|
|
calc_packet_length( PACKET *pkt )
|
|
{
|
|
u32 n = 0;
|
|
int new_ctb = 0;
|
|
|
|
log_assert (pkt->pkt.generic);
|
|
switch (pkt->pkttype)
|
|
{
|
|
case PKT_PLAINTEXT:
|
|
n = calc_plaintext (pkt->pkt.plaintext);
|
|
new_ctb = pkt->pkt.plaintext->new_ctb;
|
|
break;
|
|
case PKT_ATTRIBUTE:
|
|
case PKT_USER_ID:
|
|
case PKT_COMMENT:
|
|
case PKT_PUBLIC_KEY:
|
|
case PKT_SECRET_KEY:
|
|
case PKT_SYMKEY_ENC:
|
|
case PKT_PUBKEY_ENC:
|
|
case PKT_ENCRYPTED:
|
|
case PKT_SIGNATURE:
|
|
case PKT_ONEPASS_SIG:
|
|
case PKT_RING_TRUST:
|
|
case PKT_COMPRESSED:
|
|
default:
|
|
log_bug ("invalid packet type in calc_packet_length()");
|
|
break;
|
|
}
|
|
|
|
n += calc_header_length (n, new_ctb);
|
|
return n;
|
|
}
|
|
|
|
|
|
static gpg_error_t
|
|
write_fake_data (IOBUF out, gcry_mpi_t a)
|
|
{
|
|
unsigned int n;
|
|
void *p;
|
|
|
|
if (!a)
|
|
return 0;
|
|
if (!gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
|
|
return 0; /* e.g. due to generating a key with wrong usage. */
|
|
p = gcry_mpi_get_opaque ( a, &n);
|
|
if (!p)
|
|
return 0; /* For example due to a read error in
|
|
parse-packet.c:read_rest. */
|
|
return iobuf_write (out, p, (n+7)/8 );
|
|
}
|
|
|
|
|
|
/* Write a ring trust meta packet. */
|
|
static gpg_error_t
|
|
do_ring_trust (iobuf_t out, PKT_ring_trust *rt)
|
|
{
|
|
unsigned int namelen = 0;
|
|
unsigned int pktlen = 6;
|
|
|
|
if (rt->subtype == RING_TRUST_KEY || rt->subtype == RING_TRUST_UID)
|
|
{
|
|
if (rt->url)
|
|
namelen = strlen (rt->url);
|
|
pktlen += 1 + 4 + 1 + namelen;
|
|
}
|
|
|
|
write_header (out, (0x80 | ((PKT_RING_TRUST & 15)<<2)), pktlen);
|
|
iobuf_put (out, rt->trustval);
|
|
iobuf_put (out, rt->sigcache);
|
|
iobuf_write (out, "gpg", 3);
|
|
iobuf_put (out, rt->subtype);
|
|
if (rt->subtype == RING_TRUST_KEY || rt->subtype == RING_TRUST_UID)
|
|
{
|
|
iobuf_put (out, rt->keyorg);
|
|
write_32 (out, rt->keyupdate);
|
|
iobuf_put (out, namelen);
|
|
if (namelen)
|
|
iobuf_write (out, rt->url, namelen);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Serialize the user id (RFC 4880, Section 5.11) or the user
|
|
* attribute UID (Section 5.12) and write it to OUT.
|
|
*
|
|
* CTB is the serialization's CTB. It specifies the header format and
|
|
* the packet's type. The header length must not be set. */
|
|
static int
|
|
do_user_id( IOBUF out, int ctb, PKT_user_id *uid )
|
|
{
|
|
int rc;
|
|
int hdrlen;
|
|
|
|
log_assert (ctb_pkttype (ctb) == PKT_USER_ID
|
|
|| ctb_pkttype (ctb) == PKT_ATTRIBUTE);
|
|
|
|
/* We need to take special care of a user ID with a length of 0:
|
|
* Without forcing HDRLEN to 2 in this case an indeterminate length
|
|
* packet would be written which is not allowed. Note that we are
|
|
* always called with a CTB indicating an old packet header format,
|
|
* so that forcing a 2 octet header works. We also check for the
|
|
* maximum allowed packet size by the parser using an arbitrary
|
|
* extra 10 bytes for header data. */
|
|
if (uid->attrib_data)
|
|
{
|
|
if (uid->attrib_len > MAX_ATTR_PACKET_LENGTH - 10)
|
|
return gpg_error (GPG_ERR_TOO_LARGE);
|
|
hdrlen = uid->attrib_len? 0 : 2;
|
|
write_header2 (out, ctb, uid->attrib_len, hdrlen);
|
|
rc = iobuf_write( out, uid->attrib_data, uid->attrib_len );
|
|
}
|
|
else
|
|
{
|
|
if (uid->len > MAX_UID_PACKET_LENGTH - 10)
|
|
return gpg_error (GPG_ERR_TOO_LARGE);
|
|
hdrlen = uid->len? 0 : 2;
|
|
write_header2 (out, ctb, uid->len, hdrlen);
|
|
rc = iobuf_write( out, uid->name, uid->len );
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
/* Serialize the key (RFC 4880, Section 5.5) described by PK and write
|
|
* it to OUT.
|
|
*
|
|
* This function serializes both primary keys and subkeys with or
|
|
* without a secret part.
|
|
*
|
|
* CTB is the serialization's CTB. It specifies the header format and
|
|
* the packet's type. The header length must not be set.
|
|
*
|
|
* PK->VERSION specifies the serialization format. A value of 0 means
|
|
* to use the default version. Currently, only version 4 packets are
|
|
* supported.
|
|
*/
|
|
static int
|
|
do_key (iobuf_t out, int ctb, PKT_public_key *pk)
|
|
{
|
|
gpg_error_t err = 0;
|
|
iobuf_t a;
|
|
int i, nskey, npkey;
|
|
u32 pkbytes = 0;
|
|
int is_v5;
|
|
|
|
log_assert (pk->version == 0 || pk->version == 4 || pk->version == 5);
|
|
log_assert (ctb_pkttype (ctb) == PKT_PUBLIC_KEY
|
|
|| ctb_pkttype (ctb) == PKT_PUBLIC_SUBKEY
|
|
|| ctb_pkttype (ctb) == PKT_SECRET_KEY
|
|
|| ctb_pkttype (ctb) == PKT_SECRET_SUBKEY);
|
|
|
|
/* The length of the body is stored in the packet's header, which
|
|
* occurs before the body. Unfortunately, we don't know the length
|
|
* of the packet's body until we've written all of the data! To
|
|
* work around this, we first write the data into this temporary
|
|
* buffer, then generate the header, and finally copy the content
|
|
* of this buffer to OUT. */
|
|
a = iobuf_temp();
|
|
|
|
/* Note that the Version number, Timestamp, Algo, and the v5 Key
|
|
* material count are written at the end of the function. */
|
|
|
|
is_v5 = (pk->version == 5);
|
|
|
|
/* Get number of secret and public parameters. They are held in one
|
|
array: the public ones followed by the secret ones. */
|
|
nskey = pubkey_get_nskey (pk->pubkey_algo);
|
|
npkey = pubkey_get_npkey (pk->pubkey_algo);
|
|
|
|
/* If we don't have any public parameters - which is for example the
|
|
case if we don't know the algorithm used - the parameters are
|
|
stored as one blob in a faked (opaque) MPI. */
|
|
if (!npkey)
|
|
{
|
|
write_fake_data (a, pk->pkey[0]);
|
|
goto leave;
|
|
}
|
|
log_assert (npkey < nskey);
|
|
|
|
for (i=0; i < npkey; i++ )
|
|
{
|
|
if ( (pk->pubkey_algo == PUBKEY_ALGO_ECDSA && (i == 0))
|
|
|| (pk->pubkey_algo == PUBKEY_ALGO_EDDSA && (i == 0))
|
|
|| (pk->pubkey_algo == PUBKEY_ALGO_ECDH && (i == 0 || i == 2)))
|
|
err = gpg_mpi_write_nohdr (a, pk->pkey[i]);
|
|
else if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
|
|
|| pk->pubkey_algo == PUBKEY_ALGO_EDDSA
|
|
|| pk->pubkey_algo == PUBKEY_ALGO_ECDH)
|
|
err = sos_write (a, pk->pkey[i], NULL);
|
|
else
|
|
err = gpg_mpi_write (a, pk->pkey[i], NULL);
|
|
if (err)
|
|
goto leave;
|
|
}
|
|
|
|
/* Record the length of the public key part. */
|
|
pkbytes = iobuf_get_temp_length (a);
|
|
|
|
if (pk->seckey_info)
|
|
{
|
|
/* This is a secret key packet. */
|
|
struct seckey_info *ski = pk->seckey_info;
|
|
|
|
/* Build the header for protected (encrypted) secret parameters. */
|
|
if (ski->is_protected)
|
|
{
|
|
iobuf_put (a, ski->sha1chk? 0xfe : 0xff); /* S2k usage. */
|
|
if (is_v5)
|
|
{
|
|
/* For a v5 key determine the count of the following
|
|
* key-protection material and write it. */
|
|
int count = 1; /* Pubkey algo octet. */
|
|
if (ski->s2k.mode >= 1000)
|
|
count += 6; /* GNU specific mode descriptor. */
|
|
else
|
|
count += 2; /* Mode and hash algo. */
|
|
if (ski->s2k.mode == 1 || ski->s2k.mode == 3)
|
|
count += 8; /* Salt. */
|
|
if (ski->s2k.mode == 3)
|
|
count++; /* S2K.COUNT */
|
|
if (ski->s2k.mode != 1001 && ski->s2k.mode != 1002
|
|
&& ski->s2k.mode != 1003)
|
|
count += ski->ivlen;
|
|
|
|
iobuf_put (a, count);
|
|
}
|
|
iobuf_put (a, ski->algo); /* Pubkey algo octet. */
|
|
if (ski->s2k.mode >= 1000)
|
|
{
|
|
/* These modes are not possible in OpenPGP, we use them
|
|
to implement our extensions, 101 can be viewed as a
|
|
private/experimental extension (this is not specified
|
|
in rfc2440 but the same scheme is used for all other
|
|
algorithm identifiers). */
|
|
iobuf_put (a, 101);
|
|
iobuf_put (a, ski->s2k.hash_algo);
|
|
iobuf_write (a, "GNU", 3 );
|
|
iobuf_put (a, ski->s2k.mode - 1000);
|
|
}
|
|
else
|
|
{
|
|
iobuf_put (a, ski->s2k.mode);
|
|
iobuf_put (a, ski->s2k.hash_algo);
|
|
}
|
|
|
|
if (ski->s2k.mode == 1 || ski->s2k.mode == 3)
|
|
iobuf_write (a, ski->s2k.salt, 8);
|
|
|
|
if (ski->s2k.mode == 3)
|
|
iobuf_put (a, ski->s2k.count);
|
|
|
|
/* For our special modes 1001..1003 we do not need an IV. */
|
|
if (ski->s2k.mode != 1001 && ski->s2k.mode != 1002
|
|
&& ski->s2k.mode != 1003)
|
|
iobuf_write (a, ski->iv, ski->ivlen);
|
|
|
|
}
|
|
else /* Not protected. */
|
|
{
|
|
iobuf_put (a, 0 ); /* S2K usage = not protected. */
|
|
if (is_v5)
|
|
iobuf_put (a, 0); /* Zero octets of key-protection
|
|
* material follows. */
|
|
}
|
|
|
|
if (ski->s2k.mode == 1001)
|
|
{
|
|
/* GnuPG extension - don't write a secret key at all. */
|
|
if (is_v5)
|
|
write_32 (a, 0); /* Zero octets of key material. */
|
|
}
|
|
else if (ski->s2k.mode == 1002)
|
|
{
|
|
/* GnuPG extension - divert to OpenPGP smartcard. */
|
|
if (is_v5)
|
|
write_32 (a, 1 + ski->ivlen);
|
|
/* Length of the serial number or 0 for no serial number. */
|
|
iobuf_put (a, ski->ivlen );
|
|
/* The serial number gets stored in the IV field. */
|
|
iobuf_write (a, ski->iv, ski->ivlen);
|
|
}
|
|
else if (ski->s2k.mode == 1003)
|
|
{
|
|
/* GnuPG extension - Store raw s-expression. */
|
|
byte *p;
|
|
unsigned int ndatabits;
|
|
|
|
log_assert (gcry_mpi_get_flag (pk->pkey[npkey], GCRYMPI_FLAG_OPAQUE));
|
|
|
|
p = gcry_mpi_get_opaque (pk->pkey[npkey], &ndatabits);
|
|
/* For v5 keys we first write the number of octets of the
|
|
* following key material. */
|
|
if (is_v5)
|
|
write_32 (a, p? (ndatabits+7)/8 : 0);
|
|
if (p)
|
|
iobuf_write (a, p, (ndatabits+7)/8 );
|
|
}
|
|
else if (ski->is_protected)
|
|
{
|
|
/* The secret key is protected - write it out as it is. */
|
|
byte *p;
|
|
unsigned int ndatabits;
|
|
|
|
log_assert (gcry_mpi_get_flag (pk->pkey[npkey], GCRYMPI_FLAG_OPAQUE));
|
|
p = gcry_mpi_get_opaque (pk->pkey[npkey], &ndatabits);
|
|
/* For v5 keys we first write the number of octets of the
|
|
* following encrypted key material. */
|
|
if (is_v5)
|
|
write_32 (a, p? (ndatabits+7)/8 : 0);
|
|
if (p)
|
|
iobuf_write (a, p, (ndatabits+7)/8 );
|
|
}
|
|
else
|
|
{
|
|
/* Non-protected key. */
|
|
if (is_v5)
|
|
{
|
|
unsigned int skbytes = 0;
|
|
unsigned int n;
|
|
int j;
|
|
|
|
for (j=i; j < nskey; j++ )
|
|
{
|
|
if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
|
|
|| pk->pubkey_algo == PUBKEY_ALGO_EDDSA
|
|
|| pk->pubkey_algo == PUBKEY_ALGO_ECDH)
|
|
{
|
|
if ((err = sos_write (NULL, pk->pkey[j], &n)))
|
|
goto leave;
|
|
}
|
|
else
|
|
{
|
|
if ( (err = gpg_mpi_write (a, pk->pkey[i], NULL)))
|
|
goto leave;
|
|
}
|
|
skbytes += n;
|
|
}
|
|
|
|
write_32 (a, skbytes);
|
|
}
|
|
|
|
for ( ; i < nskey; i++ )
|
|
if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
|
|
|| pk->pubkey_algo == PUBKEY_ALGO_EDDSA
|
|
|| pk->pubkey_algo == PUBKEY_ALGO_ECDH)
|
|
{
|
|
if ((err = sos_write (a, pk->pkey[i], NULL)))
|
|
goto leave;
|
|
}
|
|
else
|
|
if ((err = gpg_mpi_write (a, pk->pkey[i], NULL)))
|
|
goto leave;
|
|
|
|
write_16 (a, ski->csum );
|
|
}
|
|
}
|
|
|
|
leave:
|
|
if (!err)
|
|
{
|
|
/* Build the header of the packet - which we must do after
|
|
* writing all the other stuff, so that we know the length of
|
|
* the packet */
|
|
u32 len = iobuf_get_temp_length (a);
|
|
len += 1; /* version number */
|
|
len += 4; /* timestamp */
|
|
len += 1; /* algo */
|
|
if (is_v5)
|
|
len += 4; /* public key material count */
|
|
|
|
write_header2 (out, ctb, len, 0);
|
|
/* And finally write it out to the real stream. */
|
|
iobuf_put (out, pk->version? pk->version : 4); /* version number */
|
|
write_32 (out, pk->timestamp );
|
|
iobuf_put (out, pk->pubkey_algo); /* algo */
|
|
if (is_v5)
|
|
write_32 (out, pkbytes); /* public key material count */
|
|
err = iobuf_write_temp (out, a); /* pub and sec key material */
|
|
}
|
|
|
|
iobuf_close (a); /* Close the temporary buffer */
|
|
return err;
|
|
}
|
|
|
|
|
|
/* Serialize the symmetric-key encrypted session key packet (RFC 4880,
|
|
* 5.3) described by ENC and write it to OUT.
|
|
*
|
|
* CTB is the serialization's CTB. It specifies the header format and
|
|
* the packet's type. The header length must not be set. */
|
|
static int
|
|
do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc )
|
|
{
|
|
int rc = 0;
|
|
IOBUF a = iobuf_temp();
|
|
|
|
log_assert (ctb_pkttype (ctb) == PKT_SYMKEY_ENC);
|
|
log_assert (enc->version == 4 || enc->version == 5);
|
|
|
|
switch (enc->s2k.mode)
|
|
{
|
|
case 0: /* Simple S2K. */
|
|
case 1: /* Salted S2K. */
|
|
case 3: /* Iterated and salted S2K. */
|
|
break; /* Reasonable values. */
|
|
|
|
default:
|
|
log_bug ("do_symkey_enc: s2k=%d\n", enc->s2k.mode);
|
|
}
|
|
iobuf_put (a, enc->version);
|
|
iobuf_put (a, enc->cipher_algo);
|
|
if (enc->version == 5)
|
|
iobuf_put (a, enc->aead_algo);
|
|
iobuf_put (a, enc->s2k.mode);
|
|
iobuf_put (a, enc->s2k.hash_algo);
|
|
if (enc->s2k.mode == 1 || enc->s2k.mode == 3)
|
|
{
|
|
iobuf_write (a, enc->s2k.salt, 8);
|
|
if (enc->s2k.mode == 3)
|
|
iobuf_put (a, enc->s2k.count);
|
|
}
|
|
if (enc->seskeylen)
|
|
iobuf_write (a, enc->seskey, enc->seskeylen);
|
|
|
|
write_header (out, ctb, iobuf_get_temp_length(a));
|
|
rc = iobuf_write_temp (out, a);
|
|
|
|
iobuf_close (a);
|
|
return rc;
|
|
}
|
|
|
|
|
|
/* Serialize the public-key encrypted session key packet (RFC 4880,
|
|
5.1) described by ENC and write it to OUT.
|
|
|
|
CTB is the serialization's CTB. It specifies the header format and
|
|
the packet's type. The header length must not be set. */
|
|
static int
|
|
do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc )
|
|
{
|
|
int rc = 0;
|
|
int n, i;
|
|
IOBUF a = iobuf_temp();
|
|
|
|
log_assert (ctb_pkttype (ctb) == PKT_PUBKEY_ENC);
|
|
|
|
iobuf_put (a, 3); /* Version. */
|
|
|
|
if ( enc->throw_keyid )
|
|
{
|
|
write_32(a, 0 ); /* Don't tell Eve who can decrypt the message. */
|
|
write_32(a, 0 );
|
|
}
|
|
else
|
|
{
|
|
write_32(a, enc->keyid[0] );
|
|
write_32(a, enc->keyid[1] );
|
|
}
|
|
iobuf_put(a,enc->pubkey_algo );
|
|
n = pubkey_get_nenc( enc->pubkey_algo );
|
|
if ( !n )
|
|
write_fake_data( a, enc->data[0] );
|
|
|
|
for (i=0; i < n && !rc ; i++ )
|
|
{
|
|
if (enc->pubkey_algo == PUBKEY_ALGO_ECDH && i == 1)
|
|
rc = gpg_mpi_write_nohdr (a, enc->data[i]);
|
|
else if (enc->pubkey_algo == PUBKEY_ALGO_ECDH)
|
|
rc = sos_write (a, enc->data[i], NULL);
|
|
else
|
|
rc = gpg_mpi_write (a, enc->data[i], NULL);
|
|
}
|
|
|
|
if (!rc)
|
|
{
|
|
write_header (out, ctb, iobuf_get_temp_length(a) );
|
|
rc = iobuf_write_temp (out, a);
|
|
}
|
|
iobuf_close(a);
|
|
return rc;
|
|
}
|
|
|
|
|
|
/* Calculate the length of the serialized plaintext packet PT (RFC
|
|
4480, Section 5.9). */
|
|
static u32
|
|
calc_plaintext( PKT_plaintext *pt )
|
|
{
|
|
/* Truncate namelen to the maximum 255 characters. Note this means
|
|
that a function that calls build_packet with an illegal literal
|
|
packet will get it back legalized. */
|
|
|
|
if(pt->namelen>255)
|
|
pt->namelen=255;
|
|
|
|
return pt->len? (1 + 1 + pt->namelen + 4 + pt->len) : 0;
|
|
}
|
|
|
|
/* Serialize the plaintext packet (RFC 4880, 5.9) described by PT and
|
|
write it to OUT.
|
|
|
|
The body of the message is stored in PT->BUF. The amount of data
|
|
to write is PT->LEN. (PT->BUF should be configured to return EOF
|
|
after this much data has been read.) If PT->LEN is 0 and CTB
|
|
indicates that this is a new format packet, then partial block mode
|
|
is assumed to have been enabled on OUT. On success, partial block
|
|
mode is disabled.
|
|
|
|
If PT->BUF is NULL, the caller must write out the data. In
|
|
this case, if PT->LEN was 0, then partial body length mode was
|
|
enabled and the caller must disable it by calling
|
|
iobuf_set_partial_body_length_mode (out, 0). */
|
|
static int
|
|
do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt )
|
|
{
|
|
int rc = 0;
|
|
size_t nbytes;
|
|
|
|
log_assert (ctb_pkttype (ctb) == PKT_PLAINTEXT);
|
|
|
|
write_header(out, ctb, calc_plaintext( pt ) );
|
|
log_assert (pt->mode == 'b' || pt->mode == 't' || pt->mode == 'u'
|
|
|| pt->mode == 'm'
|
|
|| pt->mode == 'l' || pt->mode == '1');
|
|
iobuf_put(out, pt->mode );
|
|
iobuf_put(out, pt->namelen );
|
|
iobuf_write (out, pt->name, pt->namelen);
|
|
rc = write_32(out, pt->timestamp );
|
|
if (rc)
|
|
return rc;
|
|
|
|
if (pt->buf)
|
|
{
|
|
nbytes = iobuf_copy (out, pt->buf);
|
|
if (nbytes == (size_t)(-1)
|
|
&& (iobuf_error (out) || iobuf_error (pt->buf)))
|
|
return iobuf_error (out)? iobuf_error (out):iobuf_error (pt->buf);
|
|
if(ctb_new_format_p (ctb) && !pt->len)
|
|
/* Turn off partial body length mode. */
|
|
iobuf_set_partial_body_length_mode (out, 0);
|
|
if( pt->len && nbytes != pt->len )
|
|
log_error("do_plaintext(): wrote %lu bytes but expected %lu bytes\n",
|
|
(ulong)nbytes, (ulong)pt->len );
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
/* Serialize the symmetrically encrypted data packet (RFC 4880,
|
|
Section 5.7) described by ED and write it to OUT.
|
|
|
|
Note: this only writes the packets header! The call must then
|
|
follow up and write the initial random data and the body to OUT.
|
|
(If you use the encryption iobuf filter (cipher_filter), then this
|
|
is done automatically.) */
|
|
static int
|
|
do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed )
|
|
{
|
|
int rc = 0;
|
|
u32 n;
|
|
|
|
log_assert (! ed->mdc_method);
|
|
log_assert (ctb_pkttype (ctb) == PKT_ENCRYPTED);
|
|
|
|
n = ed->len ? (ed->len + ed->extralen) : 0;
|
|
write_header(out, ctb, n );
|
|
|
|
/* This is all. The caller has to write the real data */
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* Serialize the symmetrically encrypted integrity protected data
|
|
packet (RFC 4880, Section 5.13) described by ED and write it to
|
|
OUT.
|
|
|
|
Note: this only writes the packet's header! The caller must then
|
|
follow up and write the initial random data, the body and the MDC
|
|
packet to OUT. (If you use the encryption iobuf filter
|
|
(cipher_filter), then this is done automatically.) */
|
|
static int
|
|
do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed )
|
|
{
|
|
int rc = 0;
|
|
u32 n;
|
|
|
|
log_assert (ed->mdc_method);
|
|
log_assert (ctb_pkttype (ctb) == PKT_ENCRYPTED_MDC);
|
|
|
|
/* Take version number and the following MDC packet in account. */
|
|
n = ed->len ? (ed->len + ed->extralen + 1 + 22) : 0;
|
|
write_header(out, ctb, n );
|
|
iobuf_put(out, 1 ); /* version */
|
|
|
|
/* This is all. The caller has to write the real data */
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/* Serialize the symmetrically AEAD encrypted data packet
|
|
* (rfc4880bis-03, Section 5.16) described by ED and write it to OUT.
|
|
*
|
|
* Note: this only writes only packet's header. The caller must then
|
|
* follow up and write the actual encrypted data. This should be done
|
|
* by pushing the the cipher_filter_aead. */
|
|
static int
|
|
do_encrypted_aead (iobuf_t out, int ctb, PKT_encrypted *ed)
|
|
{
|
|
u32 n;
|
|
|
|
log_assert (ctb_pkttype (ctb) == PKT_ENCRYPTED_AEAD);
|
|
|
|
n = ed->len ? (ed->len + ed->extralen + 4) : 0;
|
|
write_header (out, ctb, n );
|
|
iobuf_writebyte (out, 1); /* Version. */
|
|
iobuf_writebyte (out, ed->cipher_algo);
|
|
iobuf_writebyte (out, ed->aead_algo);
|
|
iobuf_writebyte (out, ed->chunkbyte);
|
|
|
|
/* This is all. The caller has to write the encrypted data */
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Serialize the compressed packet (RFC 4880, Section 5.6) described
|
|
by CD and write it to OUT.
|
|
|
|
Note: this only writes the packet's header! The caller must then
|
|
follow up and write the body to OUT. */
|
|
static int
|
|
do_compressed( IOBUF out, int ctb, PKT_compressed *cd )
|
|
{
|
|
int rc = 0;
|
|
|
|
log_assert (ctb_pkttype (ctb) == PKT_COMPRESSED);
|
|
|
|
/* We must use the old convention and don't use blockmode for the
|
|
sake of PGP 2 compatibility. However if the new_ctb flag was
|
|
set, CTB is already formatted as new style and write_header2
|
|
does create a partial length encoding using new the new
|
|
style. */
|
|
write_header2(out, ctb, 0, 0);
|
|
iobuf_put(out, cd->algorithm );
|
|
|
|
/* This is all. The caller has to write the real data */
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/****************
|
|
* Delete all subpackets of type REQTYPE and return a bool whether a packet
|
|
* was deleted.
|
|
*/
|
|
int
|
|
delete_sig_subpkt (subpktarea_t *area, sigsubpkttype_t reqtype )
|
|
{
|
|
int buflen;
|
|
sigsubpkttype_t type;
|
|
byte *buffer, *bufstart;
|
|
size_t n;
|
|
size_t unused = 0;
|
|
int okay = 0;
|
|
|
|
if( !area )
|
|
return 0;
|
|
buflen = area->len;
|
|
buffer = area->data;
|
|
for(;;) {
|
|
if( !buflen ) {
|
|
okay = 1;
|
|
break;
|
|
}
|
|
bufstart = buffer;
|
|
n = *buffer++; buflen--;
|
|
if( n == 255 ) {
|
|
if( buflen < 4 )
|
|
break;
|
|
n = buf32_to_size_t (buffer);
|
|
buffer += 4;
|
|
buflen -= 4;
|
|
}
|
|
else if( n >= 192 ) {
|
|
if( buflen < 2 )
|
|
break;
|
|
n = (( n - 192 ) << 8) + *buffer + 192;
|
|
buffer++;
|
|
buflen--;
|
|
}
|
|
if( buflen < n )
|
|
break;
|
|
|
|
type = *buffer & 0x7f;
|
|
if( type == reqtype ) {
|
|
buffer++;
|
|
buflen--;
|
|
n--;
|
|
if( n > buflen )
|
|
break;
|
|
buffer += n; /* point to next subpkt */
|
|
buflen -= n;
|
|
memmove (bufstart, buffer, buflen); /* shift */
|
|
unused += buffer - bufstart;
|
|
buffer = bufstart;
|
|
}
|
|
else {
|
|
buffer += n; buflen -=n;
|
|
}
|
|
}
|
|
|
|
if (!okay)
|
|
log_error ("delete_subpkt: buffer shorter than subpacket\n");
|
|
log_assert (unused <= area->len);
|
|
area->len -= unused;
|
|
return !!unused;
|
|
}
|
|
|
|
|
|
/****************
|
|
* Create or update a signature subpacket for SIG of TYPE. This
|
|
* functions knows where to put the data (hashed or unhashed). The
|
|
* function may move data from the unhashed part to the hashed one.
|
|
* Note: All pointers into sig->[un]hashed (e.g. returned by
|
|
* parse_sig_subpkt) are not valid after a call to this function. The
|
|
* data to put into the subpaket should be in a buffer with a length
|
|
* of buflen.
|
|
*/
|
|
void
|
|
build_sig_subpkt (PKT_signature *sig, sigsubpkttype_t type,
|
|
const byte *buffer, size_t buflen )
|
|
{
|
|
byte *p;
|
|
int critical, hashed;
|
|
subpktarea_t *oldarea, *newarea;
|
|
size_t nlen, n, n0;
|
|
|
|
critical = (type & SIGSUBPKT_FLAG_CRITICAL);
|
|
type &= ~SIGSUBPKT_FLAG_CRITICAL;
|
|
|
|
/* Sanity check buffer sizes */
|
|
if(parse_one_sig_subpkt(buffer,buflen,type)<0)
|
|
BUG();
|
|
|
|
switch(type)
|
|
{
|
|
case SIGSUBPKT_NOTATION:
|
|
case SIGSUBPKT_POLICY:
|
|
case SIGSUBPKT_REV_KEY:
|
|
case SIGSUBPKT_SIGNATURE:
|
|
/* we do allow multiple subpackets */
|
|
break;
|
|
|
|
default:
|
|
/* we don't allow multiple subpackets */
|
|
delete_sig_subpkt(sig->hashed,type);
|
|
delete_sig_subpkt(sig->unhashed,type);
|
|
break;
|
|
}
|
|
|
|
/* Any special magic that needs to be done for this type so the
|
|
packet doesn't need to be reparsed? */
|
|
switch(type)
|
|
{
|
|
case SIGSUBPKT_NOTATION:
|
|
sig->flags.notation=1;
|
|
break;
|
|
|
|
case SIGSUBPKT_POLICY:
|
|
sig->flags.policy_url=1;
|
|
break;
|
|
|
|
case SIGSUBPKT_PREF_KS:
|
|
sig->flags.pref_ks=1;
|
|
break;
|
|
|
|
case SIGSUBPKT_EXPORTABLE:
|
|
if(buffer[0])
|
|
sig->flags.exportable=1;
|
|
else
|
|
sig->flags.exportable=0;
|
|
break;
|
|
|
|
case SIGSUBPKT_REVOCABLE:
|
|
if(buffer[0])
|
|
sig->flags.revocable=1;
|
|
else
|
|
sig->flags.revocable=0;
|
|
break;
|
|
|
|
case SIGSUBPKT_TRUST:
|
|
sig->trust_depth=buffer[0];
|
|
sig->trust_value=buffer[1];
|
|
break;
|
|
|
|
case SIGSUBPKT_REGEXP:
|
|
sig->trust_regexp=buffer;
|
|
break;
|
|
|
|
/* This should never happen since we don't currently allow
|
|
creating such a subpacket, but just in case... */
|
|
case SIGSUBPKT_SIG_EXPIRE:
|
|
if(buf32_to_u32(buffer)+sig->timestamp<=make_timestamp())
|
|
sig->flags.expired=1;
|
|
else
|
|
sig->flags.expired=0;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if( (buflen+1) >= 8384 )
|
|
nlen = 5; /* write 5 byte length header */
|
|
else if( (buflen+1) >= 192 )
|
|
nlen = 2; /* write 2 byte length header */
|
|
else
|
|
nlen = 1; /* just a 1 byte length header */
|
|
|
|
switch( type )
|
|
{
|
|
/* The issuer being unhashed is a historical oddity. It
|
|
should work equally as well hashed. Of course, if even an
|
|
unhashed issuer is tampered with, it makes it awfully hard
|
|
to verify the sig... */
|
|
case SIGSUBPKT_ISSUER:
|
|
case SIGSUBPKT_SIGNATURE:
|
|
hashed = 0;
|
|
break;
|
|
default:
|
|
hashed = 1;
|
|
break;
|
|
}
|
|
|
|
if( critical )
|
|
type |= SIGSUBPKT_FLAG_CRITICAL;
|
|
|
|
oldarea = hashed? sig->hashed : sig->unhashed;
|
|
|
|
/* Calculate new size of the area and allocate */
|
|
n0 = oldarea? oldarea->len : 0;
|
|
n = n0 + nlen + 1 + buflen; /* length, type, buffer */
|
|
if (oldarea && n <= oldarea->size) { /* fits into the unused space */
|
|
newarea = oldarea;
|
|
/*log_debug ("updating area for type %d\n", type );*/
|
|
}
|
|
else if (oldarea) {
|
|
newarea = xrealloc (oldarea, sizeof (*newarea) + n - 1);
|
|
newarea->size = n;
|
|
/*log_debug ("reallocating area for type %d\n", type );*/
|
|
}
|
|
else {
|
|
newarea = xmalloc (sizeof (*newarea) + n - 1);
|
|
newarea->size = n;
|
|
/*log_debug ("allocating area for type %d\n", type );*/
|
|
}
|
|
newarea->len = n;
|
|
|
|
p = newarea->data + n0;
|
|
if (nlen == 5) {
|
|
*p++ = 255;
|
|
*p++ = (buflen+1) >> 24;
|
|
*p++ = (buflen+1) >> 16;
|
|
*p++ = (buflen+1) >> 8;
|
|
*p++ = (buflen+1);
|
|
*p++ = type;
|
|
memcpy (p, buffer, buflen);
|
|
}
|
|
else if (nlen == 2) {
|
|
*p++ = (buflen+1-192) / 256 + 192;
|
|
*p++ = (buflen+1-192) % 256;
|
|
*p++ = type;
|
|
memcpy (p, buffer, buflen);
|
|
}
|
|
else {
|
|
*p++ = buflen+1;
|
|
*p++ = type;
|
|
memcpy (p, buffer, buflen);
|
|
}
|
|
|
|
if (hashed)
|
|
sig->hashed = newarea;
|
|
else
|
|
sig->unhashed = newarea;
|
|
}
|
|
|
|
/*
|
|
* Put all the required stuff from SIG into subpackets of sig.
|
|
* PKSK is the signing key. SIGNHINTS are various flags like
|
|
* SIGNHINT_ADSK.
|
|
* Hmmm, should we delete those subpackets which are in a wrong area?
|
|
*/
|
|
void
|
|
build_sig_subpkt_from_sig (PKT_signature *sig, PKT_public_key *pksk,
|
|
unsigned int signhints)
|
|
{
|
|
u32 u;
|
|
byte buf[1+MAX_FINGERPRINT_LEN];
|
|
size_t fprlen;
|
|
|
|
/* For v4 keys we need to write the ISSUER subpacket. We do not
|
|
* want that for a future v5 format. We also don't write it if
|
|
* only the new RENC keyflag is set (implementations with support
|
|
* for this key flag should understand the ISSUER_FPR). */
|
|
if (pksk->version < 5 && !(signhints & SIGNHINT_ADSK))
|
|
{
|
|
u = sig->keyid[0];
|
|
buf[0] = (u >> 24) & 0xff;
|
|
buf[1] = (u >> 16) & 0xff;
|
|
buf[2] = (u >> 8) & 0xff;
|
|
buf[3] = u & 0xff;
|
|
u = sig->keyid[1];
|
|
buf[4] = (u >> 24) & 0xff;
|
|
buf[5] = (u >> 16) & 0xff;
|
|
buf[6] = (u >> 8) & 0xff;
|
|
buf[7] = u & 0xff;
|
|
build_sig_subpkt (sig, SIGSUBPKT_ISSUER, buf, 8);
|
|
}
|
|
|
|
/* Write the new ISSUER_FPR subpacket. */
|
|
fingerprint_from_pk (pksk, buf+1, &fprlen);
|
|
if (fprlen == 20 || fprlen == 32)
|
|
{
|
|
buf[0] = pksk->version;
|
|
build_sig_subpkt (sig, SIGSUBPKT_ISSUER_FPR, buf, fprlen + 1);
|
|
}
|
|
|
|
/* Write the timestamp. */
|
|
u = sig->timestamp;
|
|
buf[0] = (u >> 24) & 0xff;
|
|
buf[1] = (u >> 16) & 0xff;
|
|
buf[2] = (u >> 8) & 0xff;
|
|
buf[3] = u & 0xff;
|
|
build_sig_subpkt( sig, SIGSUBPKT_SIG_CREATED, buf, 4 );
|
|
|
|
if(sig->expiredate)
|
|
{
|
|
if(sig->expiredate>sig->timestamp)
|
|
u=sig->expiredate-sig->timestamp;
|
|
else
|
|
u=1; /* A 1-second expiration time is the shortest one
|
|
OpenPGP has */
|
|
|
|
buf[0] = (u >> 24) & 0xff;
|
|
buf[1] = (u >> 16) & 0xff;
|
|
buf[2] = (u >> 8) & 0xff;
|
|
buf[3] = u & 0xff;
|
|
|
|
/* Mark this CRITICAL, so if any implementation doesn't
|
|
understand sigs that can expire, it'll just disregard this
|
|
sig altogether. */
|
|
|
|
build_sig_subpkt( sig, SIGSUBPKT_SIG_EXPIRE | SIGSUBPKT_FLAG_CRITICAL,
|
|
buf, 4 );
|
|
}
|
|
}
|
|
|
|
void
|
|
build_attribute_subpkt(PKT_user_id *uid,byte type,
|
|
const void *buf,u32 buflen,
|
|
const void *header,u32 headerlen)
|
|
{
|
|
byte *attrib;
|
|
int idx;
|
|
|
|
if(1+headerlen+buflen>8383)
|
|
idx=5;
|
|
else if(1+headerlen+buflen>191)
|
|
idx=2;
|
|
else
|
|
idx=1;
|
|
|
|
/* realloc uid->attrib_data to the right size */
|
|
|
|
uid->attrib_data=xrealloc(uid->attrib_data,
|
|
uid->attrib_len+idx+1+headerlen+buflen);
|
|
|
|
attrib=&uid->attrib_data[uid->attrib_len];
|
|
|
|
if(idx==5)
|
|
{
|
|
attrib[0]=255;
|
|
attrib[1]=(1+headerlen+buflen) >> 24;
|
|
attrib[2]=(1+headerlen+buflen) >> 16;
|
|
attrib[3]=(1+headerlen+buflen) >> 8;
|
|
attrib[4]=1+headerlen+buflen;
|
|
}
|
|
else if(idx==2)
|
|
{
|
|
attrib[0]=(1+headerlen+buflen-192) / 256 + 192;
|
|
attrib[1]=(1+headerlen+buflen-192) % 256;
|
|
}
|
|
else
|
|
attrib[0]=1+headerlen+buflen; /* Good luck finding a JPEG this small! */
|
|
|
|
attrib[idx++]=type;
|
|
|
|
/* Tack on our data at the end */
|
|
|
|
if(headerlen>0)
|
|
memcpy(&attrib[idx],header,headerlen);
|
|
memcpy(&attrib[idx+headerlen],buf,buflen);
|
|
uid->attrib_len+=idx+headerlen+buflen;
|
|
}
|
|
|
|
/* Returns a human-readable string corresponding to the notation.
|
|
This ignores notation->value. The caller must free the result. */
|
|
static char *
|
|
notation_value_to_human_readable_string (struct notation *notation)
|
|
{
|
|
if(notation->bdat)
|
|
/* Binary data. */
|
|
{
|
|
size_t len = notation->blen;
|
|
int i;
|
|
char preview[20];
|
|
|
|
for (i = 0; i < len && i < sizeof (preview) - 1; i ++)
|
|
if (isprint (notation->bdat[i]))
|
|
preview[i] = notation->bdat[i];
|
|
else
|
|
preview[i] = '?';
|
|
preview[i] = 0;
|
|
|
|
return xasprintf (_("[ not human readable (%zu bytes: %s%s) ]"),
|
|
len, preview, i < len ? "..." : "");
|
|
}
|
|
else
|
|
/* The value is human-readable. */
|
|
return xstrdup (notation->value);
|
|
}
|
|
|
|
/* Turn the notation described by the string STRING into a notation.
|
|
|
|
STRING has the form:
|
|
|
|
- -name - Delete the notation.
|
|
- name@domain.name=value - Normal notation
|
|
- !name@domain.name=value - Notation with critical bit set.
|
|
|
|
The caller must free the result using free_notation(). */
|
|
struct notation *
|
|
string_to_notation(const char *string,int is_utf8)
|
|
{
|
|
const char *s;
|
|
int saw_at=0;
|
|
struct notation *notation;
|
|
|
|
notation=xmalloc_clear(sizeof(*notation));
|
|
|
|
if(*string=='-')
|
|
{
|
|
notation->flags.ignore=1;
|
|
string++;
|
|
}
|
|
|
|
if(*string=='!')
|
|
{
|
|
notation->flags.critical=1;
|
|
string++;
|
|
}
|
|
|
|
/* If and when the IETF assigns some official name tags, we'll have
|
|
to add them here. */
|
|
|
|
for( s=string ; *s != '='; s++ )
|
|
{
|
|
if( *s=='@')
|
|
saw_at++;
|
|
|
|
/* -notationname is legal without an = sign */
|
|
if(!*s && notation->flags.ignore)
|
|
break;
|
|
|
|
if( !*s || !isascii (*s) || (!isgraph(*s) && !isspace(*s)) )
|
|
{
|
|
log_error(_("a notation name must have only printable characters"
|
|
" or spaces, and end with an '='\n") );
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
notation->name=xmalloc((s-string)+1);
|
|
memcpy(notation->name,string,s-string);
|
|
notation->name[s-string]='\0';
|
|
|
|
if(!saw_at && !opt.expert)
|
|
{
|
|
log_error(_("a user notation name must contain the '@' character\n"));
|
|
goto fail;
|
|
}
|
|
|
|
if (saw_at > 1)
|
|
{
|
|
log_error(_("a notation name must not contain more than"
|
|
" one '@' character\n"));
|
|
goto fail;
|
|
}
|
|
|
|
if(*s)
|
|
{
|
|
const char *i=s+1;
|
|
int highbit=0;
|
|
|
|
/* we only support printable text - therefore we enforce the use
|
|
of only printable characters (an empty value is valid) */
|
|
for(s++; *s ; s++ )
|
|
{
|
|
if ( !isascii (*s) )
|
|
highbit=1;
|
|
else if (iscntrl(*s))
|
|
{
|
|
log_error(_("a notation value must not use any"
|
|
" control characters\n"));
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
if(!highbit || is_utf8)
|
|
notation->value=xstrdup(i);
|
|
else
|
|
notation->value=native_to_utf8(i);
|
|
}
|
|
|
|
return notation;
|
|
|
|
fail:
|
|
free_notation(notation);
|
|
return NULL;
|
|
}
|
|
|
|
/* Like string_to_notation, but store opaque data rather than human
|
|
readable data. */
|
|
struct notation *
|
|
blob_to_notation(const char *name, const char *data, size_t len)
|
|
{
|
|
const char *s;
|
|
int saw_at=0;
|
|
struct notation *notation;
|
|
|
|
notation=xmalloc_clear(sizeof(*notation));
|
|
|
|
if(*name=='-')
|
|
{
|
|
notation->flags.ignore=1;
|
|
name++;
|
|
}
|
|
|
|
if(*name=='!')
|
|
{
|
|
notation->flags.critical=1;
|
|
name++;
|
|
}
|
|
|
|
/* If and when the IETF assigns some official name tags, we'll have
|
|
to add them here. */
|
|
|
|
for( s=name ; *s; s++ )
|
|
{
|
|
if( *s=='@')
|
|
saw_at++;
|
|
|
|
/* -notationname is legal without an = sign */
|
|
if(!*s && notation->flags.ignore)
|
|
break;
|
|
|
|
if (*s == '=')
|
|
{
|
|
log_error(_("a notation name may not contain an '=' character\n"));
|
|
goto fail;
|
|
}
|
|
|
|
if (!isascii (*s) || (!isgraph(*s) && !isspace(*s)))
|
|
{
|
|
log_error(_("a notation name must have only printable characters"
|
|
" or spaces\n") );
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
notation->name=xstrdup (name);
|
|
|
|
if(!saw_at && !opt.expert)
|
|
{
|
|
log_error(_("a user notation name must contain the '@' character\n"));
|
|
goto fail;
|
|
}
|
|
|
|
if (saw_at > 1)
|
|
{
|
|
log_error(_("a notation name must not contain more than"
|
|
" one '@' character\n"));
|
|
goto fail;
|
|
}
|
|
|
|
notation->bdat = xmalloc (len);
|
|
memcpy (notation->bdat, data, len);
|
|
notation->blen = len;
|
|
|
|
notation->value = notation_value_to_human_readable_string (notation);
|
|
|
|
return notation;
|
|
|
|
fail:
|
|
free_notation(notation);
|
|
return NULL;
|
|
}
|
|
|
|
struct notation *
|
|
sig_to_notation(PKT_signature *sig)
|
|
{
|
|
const byte *p;
|
|
size_t len;
|
|
int seq = 0;
|
|
int crit;
|
|
notation_t list = NULL;
|
|
|
|
/* See RFC 4880, 5.2.3.16 for the format of notation data. In
|
|
short, a notation has:
|
|
|
|
- 4 bytes of flags
|
|
- 2 byte name length (n1)
|
|
- 2 byte value length (n2)
|
|
- n1 bytes of name data
|
|
- n2 bytes of value data
|
|
*/
|
|
while((p=enum_sig_subpkt (sig, 1, SIGSUBPKT_NOTATION, &len, &seq, &crit)))
|
|
{
|
|
int n1,n2;
|
|
struct notation *n=NULL;
|
|
|
|
if(len<8)
|
|
{
|
|
log_info(_("WARNING: invalid notation data found\n"));
|
|
continue;
|
|
}
|
|
|
|
/* name length. */
|
|
n1=(p[4]<<8)|p[5];
|
|
/* value length. */
|
|
n2=(p[6]<<8)|p[7];
|
|
|
|
if(8+n1+n2!=len)
|
|
{
|
|
log_info(_("WARNING: invalid notation data found\n"));
|
|
continue;
|
|
}
|
|
|
|
n=xmalloc_clear(sizeof(*n));
|
|
n->name=xmalloc(n1+1);
|
|
|
|
memcpy(n->name,&p[8],n1);
|
|
n->name[n1]='\0';
|
|
|
|
if(p[0]&0x80)
|
|
/* The value is human-readable. */
|
|
{
|
|
n->value=xmalloc(n2+1);
|
|
memcpy(n->value,&p[8+n1],n2);
|
|
n->value[n2]='\0';
|
|
n->flags.human = 1;
|
|
}
|
|
else
|
|
/* Binary data. */
|
|
{
|
|
n->bdat=xmalloc(n2);
|
|
n->blen=n2;
|
|
memcpy(n->bdat,&p[8+n1],n2);
|
|
|
|
n->value = notation_value_to_human_readable_string (n);
|
|
}
|
|
|
|
n->flags.critical=crit;
|
|
|
|
n->next=list;
|
|
list=n;
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
/* Release the resources associated with the *list* of notations. To
|
|
release a single notation, make sure that notation->next is
|
|
NULL. */
|
|
void
|
|
free_notation(struct notation *notation)
|
|
{
|
|
while(notation)
|
|
{
|
|
struct notation *n=notation;
|
|
|
|
xfree(n->name);
|
|
xfree(n->value);
|
|
xfree(n->altvalue);
|
|
xfree(n->bdat);
|
|
notation=n->next;
|
|
xfree(n);
|
|
}
|
|
}
|
|
|
|
/* Serialize the signature packet (RFC 4880, Section 5.2) described by
|
|
SIG and write it to OUT. */
|
|
static int
|
|
do_signature( IOBUF out, int ctb, PKT_signature *sig )
|
|
{
|
|
int rc = 0;
|
|
int n, i;
|
|
IOBUF a = iobuf_temp();
|
|
|
|
log_assert (ctb_pkttype (ctb) == PKT_SIGNATURE);
|
|
|
|
if ( !sig->version || sig->version == 3)
|
|
{
|
|
iobuf_put( a, 3 );
|
|
|
|
/* Version 3 packets don't support subpackets. Actually we
|
|
* should never get to here but real life is different and thus
|
|
* we now use a log_fatal instead of a log_assert here. */
|
|
if (sig->hashed || sig->unhashed)
|
|
log_fatal ("trying to write a subpacket to a v3 signature (%d,%d)\n",
|
|
!!sig->hashed, !!sig->unhashed);
|
|
}
|
|
else
|
|
iobuf_put( a, sig->version );
|
|
if ( sig->version < 4 )
|
|
iobuf_put (a, 5 ); /* Constant used by pre-v4 signatures. */
|
|
iobuf_put (a, sig->sig_class );
|
|
if ( sig->version < 4 )
|
|
{
|
|
write_32(a, sig->timestamp );
|
|
write_32(a, sig->keyid[0] );
|
|
write_32(a, sig->keyid[1] );
|
|
}
|
|
iobuf_put(a, sig->pubkey_algo );
|
|
iobuf_put(a, sig->digest_algo );
|
|
if ( sig->version >= 4 )
|
|
{
|
|
size_t nn;
|
|
/* Timestamp and keyid must have been packed into the subpackets
|
|
prior to the call of this function, because these subpackets
|
|
are hashed. */
|
|
nn = sig->hashed? sig->hashed->len : 0;
|
|
write_16(a, nn);
|
|
if (nn)
|
|
iobuf_write( a, sig->hashed->data, nn );
|
|
nn = sig->unhashed? sig->unhashed->len : 0;
|
|
write_16(a, nn);
|
|
if (nn)
|
|
iobuf_write( a, sig->unhashed->data, nn );
|
|
}
|
|
iobuf_put(a, sig->digest_start[0] );
|
|
iobuf_put(a, sig->digest_start[1] );
|
|
n = pubkey_get_nsig( sig->pubkey_algo );
|
|
if ( !n )
|
|
write_fake_data( a, sig->data[0] );
|
|
if (sig->pubkey_algo == PUBKEY_ALGO_ECDSA
|
|
|| sig->pubkey_algo == PUBKEY_ALGO_EDDSA)
|
|
for (i=0; i < n && !rc ; i++ )
|
|
rc = sos_write (a, sig->data[i], NULL);
|
|
else
|
|
for (i=0; i < n && !rc ; i++ )
|
|
rc = gpg_mpi_write (a, sig->data[i], NULL);
|
|
|
|
if (!rc)
|
|
{
|
|
if ( is_RSA(sig->pubkey_algo) && sig->version < 4 )
|
|
write_sign_packet_header(out, ctb, iobuf_get_temp_length(a) );
|
|
else
|
|
write_header(out, ctb, iobuf_get_temp_length(a) );
|
|
rc = iobuf_write_temp( out, a );
|
|
}
|
|
|
|
iobuf_close(a);
|
|
return rc;
|
|
}
|
|
|
|
|
|
/* Serialize the one-pass signature packet (RFC 4880, Section 5.4)
|
|
described by OPS and write it to OUT. */
|
|
static int
|
|
do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops )
|
|
{
|
|
log_assert (ctb_pkttype (ctb) == PKT_ONEPASS_SIG);
|
|
|
|
write_header(out, ctb, 4 + 8 + 1);
|
|
|
|
iobuf_put (out, 3); /* Version. */
|
|
iobuf_put(out, ops->sig_class );
|
|
iobuf_put(out, ops->digest_algo );
|
|
iobuf_put(out, ops->pubkey_algo );
|
|
write_32(out, ops->keyid[0] );
|
|
write_32(out, ops->keyid[1] );
|
|
iobuf_put(out, ops->last );
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Write a 16-bit quantity to OUT in big endian order. */
|
|
static int
|
|
write_16(IOBUF out, u16 a)
|
|
{
|
|
iobuf_put(out, a>>8);
|
|
if( iobuf_put(out,a) )
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
/* Write a 32-bit quantity to OUT in big endian order. */
|
|
static int
|
|
write_32(IOBUF out, u32 a)
|
|
{
|
|
iobuf_put(out, a>> 24);
|
|
iobuf_put(out, a>> 16);
|
|
iobuf_put(out, a>> 8);
|
|
return iobuf_put(out, a);
|
|
}
|
|
|
|
|
|
/****************
|
|
* calculate the length of a header.
|
|
*
|
|
* LEN is the length of the packet's body. NEW_CTB is whether we are
|
|
* using a new or old format packet.
|
|
*
|
|
* This function does not handle indeterminate lengths or partial body
|
|
* lengths. (If you pass LEN as 0, then this function assumes you
|
|
* really mean an empty body.)
|
|
*/
|
|
static int
|
|
calc_header_length( u32 len, int new_ctb )
|
|
{
|
|
if( new_ctb ) {
|
|
if( len < 192 )
|
|
return 2;
|
|
if( len < 8384 )
|
|
return 3;
|
|
else
|
|
return 6;
|
|
}
|
|
if( len < 256 )
|
|
return 2;
|
|
if( len < 65536 )
|
|
return 3;
|
|
|
|
return 5;
|
|
}
|
|
|
|
/****************
|
|
* Write the CTB and the packet length
|
|
*/
|
|
static int
|
|
write_header( IOBUF out, int ctb, u32 len )
|
|
{
|
|
return write_header2( out, ctb, len, 0 );
|
|
}
|
|
|
|
|
|
static int
|
|
write_sign_packet_header (IOBUF out, int ctb, u32 len)
|
|
{
|
|
(void)ctb;
|
|
|
|
/* Work around a bug in the pgp read function for signature packets,
|
|
which are not correctly coded and silently assume at some point 2
|
|
byte length headers.*/
|
|
iobuf_put (out, 0x89 );
|
|
iobuf_put (out, len >> 8 );
|
|
return iobuf_put (out, len) == -1 ? -1:0;
|
|
}
|
|
|
|
/****************
|
|
* Write a packet header to OUT.
|
|
*
|
|
* CTB is the ctb. It determines whether a new or old format packet
|
|
* header should be written. The length field is adjusted, but the
|
|
* CTB is otherwise written out as is.
|
|
*
|
|
* LEN is the length of the packet's body.
|
|
*
|
|
* If HDRLEN is set, then we don't necessarily use the most efficient
|
|
* encoding to store LEN, but the specified length. (If this is not
|
|
* possible, this is a bug.) In this case, LEN=0 means a 0 length
|
|
* packet. Note: setting HDRLEN is only supported for old format
|
|
* packets!
|
|
*
|
|
* If HDRLEN is not set, then the shortest encoding is used. In this
|
|
* case, LEN=0 means the body has an indeterminate length and a
|
|
* partial body length header (if a new format packet) or an
|
|
* indeterminate length header (if an old format packet) is written
|
|
* out. Further, if using partial body lengths, this enables partial
|
|
* body length mode on OUT.
|
|
*/
|
|
static int
|
|
write_header2( IOBUF out, int ctb, u32 len, int hdrlen )
|
|
{
|
|
if (ctb_new_format_p (ctb))
|
|
return write_new_header( out, ctb, len, hdrlen );
|
|
|
|
/* An old format packet. Refer to RFC 4880, Section 4.2.1 to
|
|
understand how lengths are encoded in this case. */
|
|
|
|
/* The length encoding is stored in the two least significant bits.
|
|
Make sure they are cleared. */
|
|
log_assert ((ctb & 3) == 0);
|
|
|
|
log_assert (hdrlen == 0 || hdrlen == 2 || hdrlen == 3 || hdrlen == 5);
|
|
|
|
if (hdrlen)
|
|
/* Header length is given. */
|
|
{
|
|
if( hdrlen == 2 && len < 256 )
|
|
/* 00 => 1 byte length. */
|
|
;
|
|
else if( hdrlen == 3 && len < 65536 )
|
|
/* 01 => 2 byte length. If len < 256, this is not the most
|
|
compact encoding, but it is a correct encoding. */
|
|
ctb |= 1;
|
|
else if (hdrlen == 5)
|
|
/* 10 => 4 byte length. If len < 65536, this is not the most
|
|
compact encoding, but it is a correct encoding. */
|
|
ctb |= 2;
|
|
else
|
|
log_bug ("Can't encode length=%d in a %d byte header!\n",
|
|
len, hdrlen);
|
|
}
|
|
else
|
|
{
|
|
if( !len )
|
|
/* 11 => Indeterminate length. */
|
|
ctb |= 3;
|
|
else if( len < 256 )
|
|
/* 00 => 1 byte length. */
|
|
;
|
|
else if( len < 65536 )
|
|
/* 01 => 2 byte length. */
|
|
ctb |= 1;
|
|
else
|
|
/* 10 => 4 byte length. */
|
|
ctb |= 2;
|
|
}
|
|
|
|
if( iobuf_put(out, ctb ) )
|
|
return -1;
|
|
|
|
if( len || hdrlen )
|
|
{
|
|
if( ctb & 2 )
|
|
{
|
|
if(iobuf_put(out, len >> 24 ))
|
|
return -1;
|
|
if(iobuf_put(out, len >> 16 ))
|
|
return -1;
|
|
}
|
|
|
|
if( ctb & 3 )
|
|
if(iobuf_put(out, len >> 8 ))
|
|
return -1;
|
|
|
|
if( iobuf_put(out, len ) )
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Write a new format header to OUT.
|
|
|
|
CTB is the ctb.
|
|
|
|
LEN is the length of the packet's body. If LEN is 0, then enables
|
|
partial body length mode (i.e., the body is of an indeterminant
|
|
length) on OUT. Note: this function cannot be used to generate a
|
|
header for a zero length packet.
|
|
|
|
HDRLEN is the length of the packet's header. If HDRLEN is 0, the
|
|
shortest encoding is chosen based on the length of the packet's
|
|
body. Currently, values other than 0 are not supported.
|
|
|
|
Returns 0 on success. */
|
|
static int
|
|
write_new_header( IOBUF out, int ctb, u32 len, int hdrlen )
|
|
{
|
|
if( hdrlen )
|
|
log_bug("can't cope with hdrlen yet\n");
|
|
|
|
if( iobuf_put(out, ctb ) )
|
|
return -1;
|
|
if( !len ) {
|
|
iobuf_set_partial_body_length_mode(out, 512 );
|
|
}
|
|
else {
|
|
if( len < 192 ) {
|
|
if( iobuf_put(out, len ) )
|
|
return -1;
|
|
}
|
|
else if( len < 8384 ) {
|
|
len -= 192;
|
|
if( iobuf_put( out, (len / 256) + 192) )
|
|
return -1;
|
|
if( iobuf_put( out, (len % 256) ) )
|
|
return -1;
|
|
}
|
|
else {
|
|
if( iobuf_put( out, 0xff ) )
|
|
return -1;
|
|
if( iobuf_put( out, (len >> 24)&0xff ) )
|
|
return -1;
|
|
if( iobuf_put( out, (len >> 16)&0xff ) )
|
|
return -1;
|
|
if( iobuf_put( out, (len >> 8)&0xff ) )
|
|
return -1;
|
|
if( iobuf_put( out, len & 0xff ) )
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|