mirror of
https://git.gnunet.org/libmicrohttpd.git
synced 2026-09-25 04:09:31 +03:00
removing dead stuff
This commit is contained in:
@@ -218,24 +218,6 @@ AC_ARG_ENABLE([x509],
|
||||
AC_MSG_RESULT($enable_x509)
|
||||
|
||||
|
||||
# optional: OpenPGP support
|
||||
AC_MSG_CHECKING(--enable-OpenPGP argument)
|
||||
AC_ARG_ENABLE([OpenPGP],
|
||||
[AS_HELP_STRING([--enable-OpenPGP],
|
||||
[enable OpenPGP support (default is no)])],
|
||||
[enable_openpgp=$enableval],
|
||||
[enable_openpgp="no"])
|
||||
AC_MSG_RESULT($enable_openpgp)
|
||||
# currently we ignore this option.
|
||||
if test "$enable_openpgp" = "yes"
|
||||
then
|
||||
AC_DEFINE([ENABLE_OPENPGP],[0],[Include OpenGPG support])
|
||||
else
|
||||
AC_DEFINE([ENABLE_OPENPGP],[0],[Include OpenGPG support])
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_OPENPGP, test "$enable_openpgp" = "yes")
|
||||
|
||||
|
||||
# Libgcrypt linkage : required for HTTPS support
|
||||
AC_CHECK_HEADERS(gcrypt.h,gcrypt=true,gcrypt=false)
|
||||
AC_ARG_WITH(libgcrypt,
|
||||
@@ -342,7 +324,6 @@ AC_MSG_NOTICE([Configured to build curl/libcurl:
|
||||
HTTPS support: ${enable_HTTPS}
|
||||
TLS support: ${enable_TLS}
|
||||
SSLv3 support: ${enable_SSL}
|
||||
OpenGPG support: ${enable_openpgp}
|
||||
x509 support: ${enable_x509}
|
||||
libgcrypt: ${MSG_GCRYPT}
|
||||
|
||||
|
||||
@@ -42,9 +42,7 @@ libmicrohttpd_la_LIBADD = \
|
||||
https/lgl/liblgl.la \
|
||||
https/x509/libx509.la \
|
||||
https/tls/libtls.la \
|
||||
https/minitasn1/libasn1.la \
|
||||
https/opencdk/libopencdk.la \
|
||||
https/openpgp/libopenpgp.la
|
||||
https/minitasn1/libasn1.la
|
||||
endif
|
||||
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
* @author Christian Grothoff
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "platform.h"
|
||||
#include "platform.h"
|
||||
#include "microhttpd.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -1,6 +1 @@
|
||||
# placing '.' at the end of SUBDIRS having OPENPGP enabled mixes up build order !
|
||||
SUBDIRS = minitasn1 lgl x509 tls
|
||||
|
||||
if ENABLE_OPENPGP
|
||||
# SUBDIRS += opencdk openpgp
|
||||
endif
|
||||
SUBDIRS = minitasn1 lgl x509 tls .
|
||||
|
||||
@@ -1,186 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation
|
||||
*
|
||||
* Author: Nikos Mavrogiannopoulos
|
||||
*
|
||||
* This file is part of GNUTLS-EXTRA.
|
||||
*
|
||||
* GNUTLS-EXTRA 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.
|
||||
*
|
||||
* GNUTLS-EXTRA 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 GNUTLS-EXTRA; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Note the libgnutls-extra is not a standalone library. It requires
|
||||
* to link also against libgnutls.
|
||||
*/
|
||||
|
||||
#ifndef GNUTLS_EXTRA_H
|
||||
#define GNUTLS_EXTRA_H
|
||||
|
||||
#include "gnutls.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#define LIBGNUTLS_EXTRA_VERSION LIBGNUTLS_VERSION
|
||||
|
||||
/* Openpgp certificate stuff
|
||||
*/
|
||||
|
||||
typedef enum gnutls_openpgp_crt_fmt
|
||||
{ GNUTLS_OPENPGP_FMT_RAW,
|
||||
GNUTLS_OPENPGP_FMT_BASE64
|
||||
} gnutls_openpgp_crt_fmt_t;
|
||||
|
||||
/**
|
||||
* mhd_gtls_openpgp_recv_key_func - Callback prototype to get OpenPGP keys
|
||||
* @session: a TLS session
|
||||
* @keyfpr: key fingerprint
|
||||
* @keyfpr_length: length of key fingerprint
|
||||
* @key: output key.
|
||||
*
|
||||
* A callback of this type is used to retrieve OpenPGP keys. Only
|
||||
* useful on the server, and will only be used if the peer send a key
|
||||
* fingerprint instead of a full key. See also
|
||||
* gnutls_openpgp_set_recv_key_function().
|
||||
*
|
||||
*/
|
||||
typedef int (*mhd_gtls_openpgp_recv_key_func) (mhd_gtls_session_t session,
|
||||
const unsigned char *keyfpr,
|
||||
unsigned int keyfpr_length,
|
||||
gnutls_datum_t * key);
|
||||
|
||||
void gnutls_openpgp_set_recv_key_function (mhd_gtls_session_t session,
|
||||
mhd_gtls_openpgp_recv_key_func
|
||||
func);
|
||||
|
||||
int
|
||||
gnutls_certificate_set_openpgp_key_file (mhd_gtls_cert_credentials_t
|
||||
res, const char *CERTFILE,
|
||||
const char *KEYFILE,
|
||||
gnutls_openpgp_crt_fmt_t);
|
||||
int gnutls_certificate_set_openpgp_key_mem (mhd_gtls_cert_credentials_t res,
|
||||
const gnutls_datum_t * CERT,
|
||||
const gnutls_datum_t * KEY,
|
||||
gnutls_openpgp_crt_fmt_t);
|
||||
|
||||
int
|
||||
gnutls_certificate_set_openpgp_keyring_mem
|
||||
(mhd_gtls_cert_credentials_t c, const unsigned char *data,
|
||||
size_t dlen, gnutls_openpgp_crt_fmt_t);
|
||||
|
||||
int
|
||||
gnutls_certificate_set_openpgp_keyring_file
|
||||
(mhd_gtls_cert_credentials_t c, const char *file,
|
||||
gnutls_openpgp_crt_fmt_t);
|
||||
|
||||
/*
|
||||
* TLS/IA stuff
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GNUTLS_IA_APPLICATION_PAYLOAD = 0,
|
||||
GNUTLS_IA_INTERMEDIATE_PHASE_FINISHED = 1,
|
||||
GNUTLS_IA_FINAL_PHASE_FINISHED = 2
|
||||
} gnutls_ia_apptype_t;
|
||||
|
||||
/*
|
||||
* TLS/IA credential
|
||||
*/
|
||||
typedef int (*gnutls_ia_avp_func) (mhd_gtls_session_t session, void *ptr,
|
||||
const char *last, size_t lastlen,
|
||||
char **next, size_t * nextlen);
|
||||
|
||||
typedef struct gnutls_ia_server_credentials_st
|
||||
*gnutls_ia_server_credentials_t;
|
||||
typedef struct gnutls_ia_client_credentials_st
|
||||
*gnutls_ia_client_credentials_t;
|
||||
|
||||
/* Allocate and free TLS/IA credentials. */
|
||||
extern void
|
||||
gnutls_ia_free_client_credentials (gnutls_ia_client_credentials_t sc);
|
||||
extern int
|
||||
gnutls_ia_allocate_client_credentials (gnutls_ia_client_credentials_t *
|
||||
sc);
|
||||
|
||||
extern void
|
||||
gnutls_ia_free_server_credentials (gnutls_ia_server_credentials_t sc);
|
||||
extern int
|
||||
gnutls_ia_allocate_server_credentials (gnutls_ia_server_credentials_t *
|
||||
sc);
|
||||
|
||||
/* Client TLS/IA credential functions. */
|
||||
extern void
|
||||
gnutls_ia_set_client_avp_function (gnutls_ia_client_credentials_t cred,
|
||||
gnutls_ia_avp_func avp_func);
|
||||
extern void
|
||||
gnutls_ia_set_client_avp_ptr (gnutls_ia_client_credentials_t cred,
|
||||
void *ptr);
|
||||
extern void *gnutls_ia_get_client_avp_ptr (gnutls_ia_client_credentials_t
|
||||
cred);
|
||||
|
||||
/* Server TLS/IA credential functions. */
|
||||
extern void
|
||||
gnutls_ia_set_server_avp_function (gnutls_ia_server_credentials_t cred,
|
||||
gnutls_ia_avp_func avp_func);
|
||||
extern void
|
||||
gnutls_ia_set_server_avp_ptr (gnutls_ia_server_credentials_t cred,
|
||||
void *ptr);
|
||||
extern void *gnutls_ia_get_server_avp_ptr (gnutls_ia_server_credentials_t
|
||||
cred);
|
||||
|
||||
/* TLS/IA handshake. */
|
||||
extern int gnutls_ia_handshake_p (mhd_gtls_session_t session);
|
||||
|
||||
extern int gnutls_ia_handshake (mhd_gtls_session_t session);
|
||||
|
||||
/* TLS/IA low level interface. */
|
||||
extern int
|
||||
gnutls_ia_permute_inner_secret (mhd_gtls_session_t session,
|
||||
size_t session_keys_size,
|
||||
const char *session_keys);
|
||||
extern int gnutls_ia_endphase_send (mhd_gtls_session_t session,
|
||||
int final_p);
|
||||
|
||||
extern int gnutls_ia_verify_endphase (mhd_gtls_session_t session,
|
||||
const char *checksum);
|
||||
|
||||
extern ssize_t gnutls_ia_send (mhd_gtls_session_t session,
|
||||
const char *data, size_t sizeofdata);
|
||||
extern ssize_t gnutls_ia_recv (mhd_gtls_session_t session,
|
||||
char *data, size_t sizeofdata);
|
||||
|
||||
/* Utility stuff. */
|
||||
extern int gnutls_ia_generate_challenge (mhd_gtls_session_t session,
|
||||
size_t buffer_size, char *buffer);
|
||||
extern void gnutls_ia_extract_inner_secret (mhd_gtls_session_t session,
|
||||
char *buffer);
|
||||
|
||||
/* Define whether inner phases are wanted. */
|
||||
extern void gnutls_ia_enable (mhd_gtls_session_t session,
|
||||
int allow_skip_on_resume);
|
||||
|
||||
int gnutls_global_init_extra (void);
|
||||
|
||||
/* returns libgnutls-extra version (call it with a NULL argument)
|
||||
*/
|
||||
const char *gnutls_extra_check_version (const char *req_version);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,14 +0,0 @@
|
||||
AM_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src/include \
|
||||
-I$(top_srcdir)/lib \
|
||||
-I$(top_srcdir)/lgl \
|
||||
-I$(GCRYPT_CPPFLAGS)
|
||||
|
||||
noinst_LTLIBRARIES = libopencdk.la
|
||||
|
||||
libopencdk_la_LDFLAGS = -lgcrypt
|
||||
|
||||
libopencdk_la_SOURCES = armor.c filters.h main.c seskey.c types.h \
|
||||
cipher.c kbnode.c main.h packet.h dummy.c sig-check.c verify.c \
|
||||
compress.c keydb.c misc.c pubkey.c stream.c write-packet.c \
|
||||
context.h literal.c new-packet.c read-packet.c stream.h opencdk.h
|
||||
@@ -1,5 +0,0 @@
|
||||
This is a stripped down mirror of the files in OpenCDK
|
||||
src/. To avoid to link proc-packets.c, dummy.c is included.
|
||||
|
||||
In Makefile.am libminiopencdk_la_SOURCES contains the list
|
||||
of all needed files.
|
||||
@@ -1,763 +0,0 @@
|
||||
/* armor.c - Armor filters
|
||||
* Copyright (C) 2001, 2002, 2003, 2007 Timo Schulz
|
||||
* Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*
|
||||
* ChangeLog for basic BASE64 code (base64_encode, base64_decode):
|
||||
* Original author: Eric S. Raymond (Fetchmail)
|
||||
* Heavily modified by Brendan Cully <brendan@kublai.com> (Mutt)
|
||||
* Modify the code for generic use by Timo Schulz <twoaday@freakmail.de>
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "opencdk.h"
|
||||
#include "main.h"
|
||||
#include "filters.h"
|
||||
|
||||
#ifdef __MINGW32__
|
||||
# define LF "\r\n"
|
||||
#else
|
||||
# define LF "\n"
|
||||
#endif
|
||||
|
||||
#define CRCINIT 0xB704CE
|
||||
|
||||
#define BAD -1
|
||||
#define b64val(c) index64[(unsigned int)(c)]
|
||||
|
||||
static u32 crc_table[] = {
|
||||
0x000000, 0x864CFB, 0x8AD50D, 0x0C99F6, 0x93E6E1, 0x15AA1A, 0x1933EC,
|
||||
0x9F7F17,
|
||||
0xA18139, 0x27CDC2, 0x2B5434, 0xAD18CF, 0x3267D8, 0xB42B23, 0xB8B2D5,
|
||||
0x3EFE2E,
|
||||
0xC54E89, 0x430272, 0x4F9B84, 0xC9D77F, 0x56A868, 0xD0E493, 0xDC7D65,
|
||||
0x5A319E,
|
||||
0x64CFB0, 0xE2834B, 0xEE1ABD, 0x685646, 0xF72951, 0x7165AA, 0x7DFC5C,
|
||||
0xFBB0A7,
|
||||
0x0CD1E9, 0x8A9D12, 0x8604E4, 0x00481F, 0x9F3708, 0x197BF3, 0x15E205,
|
||||
0x93AEFE,
|
||||
0xAD50D0, 0x2B1C2B, 0x2785DD, 0xA1C926, 0x3EB631, 0xB8FACA, 0xB4633C,
|
||||
0x322FC7,
|
||||
0xC99F60, 0x4FD39B, 0x434A6D, 0xC50696, 0x5A7981, 0xDC357A, 0xD0AC8C,
|
||||
0x56E077,
|
||||
0x681E59, 0xEE52A2, 0xE2CB54, 0x6487AF, 0xFBF8B8, 0x7DB443, 0x712DB5,
|
||||
0xF7614E,
|
||||
0x19A3D2, 0x9FEF29, 0x9376DF, 0x153A24, 0x8A4533, 0x0C09C8, 0x00903E,
|
||||
0x86DCC5,
|
||||
0xB822EB, 0x3E6E10, 0x32F7E6, 0xB4BB1D, 0x2BC40A, 0xAD88F1, 0xA11107,
|
||||
0x275DFC,
|
||||
0xDCED5B, 0x5AA1A0, 0x563856, 0xD074AD, 0x4F0BBA, 0xC94741, 0xC5DEB7,
|
||||
0x43924C,
|
||||
0x7D6C62, 0xFB2099, 0xF7B96F, 0x71F594, 0xEE8A83, 0x68C678, 0x645F8E,
|
||||
0xE21375,
|
||||
0x15723B, 0x933EC0, 0x9FA736, 0x19EBCD, 0x8694DA, 0x00D821, 0x0C41D7,
|
||||
0x8A0D2C,
|
||||
0xB4F302, 0x32BFF9, 0x3E260F, 0xB86AF4, 0x2715E3, 0xA15918, 0xADC0EE,
|
||||
0x2B8C15,
|
||||
0xD03CB2, 0x567049, 0x5AE9BF, 0xDCA544, 0x43DA53, 0xC596A8, 0xC90F5E,
|
||||
0x4F43A5,
|
||||
0x71BD8B, 0xF7F170, 0xFB6886, 0x7D247D, 0xE25B6A, 0x641791, 0x688E67,
|
||||
0xEEC29C,
|
||||
0x3347A4, 0xB50B5F, 0xB992A9, 0x3FDE52, 0xA0A145, 0x26EDBE, 0x2A7448,
|
||||
0xAC38B3,
|
||||
0x92C69D, 0x148A66, 0x181390, 0x9E5F6B, 0x01207C, 0x876C87, 0x8BF571,
|
||||
0x0DB98A,
|
||||
0xF6092D, 0x7045D6, 0x7CDC20, 0xFA90DB, 0x65EFCC, 0xE3A337, 0xEF3AC1,
|
||||
0x69763A,
|
||||
0x578814, 0xD1C4EF, 0xDD5D19, 0x5B11E2, 0xC46EF5, 0x42220E, 0x4EBBF8,
|
||||
0xC8F703,
|
||||
0x3F964D, 0xB9DAB6, 0xB54340, 0x330FBB, 0xAC70AC, 0x2A3C57, 0x26A5A1,
|
||||
0xA0E95A,
|
||||
0x9E1774, 0x185B8F, 0x14C279, 0x928E82, 0x0DF195, 0x8BBD6E, 0x872498,
|
||||
0x016863,
|
||||
0xFAD8C4, 0x7C943F, 0x700DC9, 0xF64132, 0x693E25, 0xEF72DE, 0xE3EB28,
|
||||
0x65A7D3,
|
||||
0x5B59FD, 0xDD1506, 0xD18CF0, 0x57C00B, 0xC8BF1C, 0x4EF3E7, 0x426A11,
|
||||
0xC426EA,
|
||||
0x2AE476, 0xACA88D, 0xA0317B, 0x267D80, 0xB90297, 0x3F4E6C, 0x33D79A,
|
||||
0xB59B61,
|
||||
0x8B654F, 0x0D29B4, 0x01B042, 0x87FCB9, 0x1883AE, 0x9ECF55, 0x9256A3,
|
||||
0x141A58,
|
||||
0xEFAAFF, 0x69E604, 0x657FF2, 0xE33309, 0x7C4C1E, 0xFA00E5, 0xF69913,
|
||||
0x70D5E8,
|
||||
0x4E2BC6, 0xC8673D, 0xC4FECB, 0x42B230, 0xDDCD27, 0x5B81DC, 0x57182A,
|
||||
0xD154D1,
|
||||
0x26359F, 0xA07964, 0xACE092, 0x2AAC69, 0xB5D37E, 0x339F85, 0x3F0673,
|
||||
0xB94A88,
|
||||
0x87B4A6, 0x01F85D, 0x0D61AB, 0x8B2D50, 0x145247, 0x921EBC, 0x9E874A,
|
||||
0x18CBB1,
|
||||
0xE37B16, 0x6537ED, 0x69AE1B, 0xEFE2E0, 0x709DF7, 0xF6D10C, 0xFA48FA,
|
||||
0x7C0401,
|
||||
0x42FA2F, 0xC4B6D4, 0xC82F22, 0x4E63D9, 0xD11CCE, 0x575035, 0x5BC9C3,
|
||||
0xDD8538
|
||||
};
|
||||
|
||||
static const char *armor_begin[] = {
|
||||
"BEGIN PGP MESSAGE",
|
||||
"BEGIN PGP PUBLIC KEY BLOCK",
|
||||
"BEGIN PGP PRIVATE KEY BLOCK",
|
||||
"BEGIN PGP SIGNATURE",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char *armor_end[] = {
|
||||
"END PGP MESSAGE",
|
||||
"END PGP PUBLIC KEY BLOCK",
|
||||
"END PGP PRIVATE KEY BLOCK",
|
||||
"END PGP SIGNATURE",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char *valid_headers[] = {
|
||||
"Comment",
|
||||
"Version",
|
||||
"MessageID",
|
||||
"Hash",
|
||||
"Charset",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char b64chars[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
static int index64[128] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
|
||||
/* encode a raw binary buffer to a null-terminated base64 strings */
|
||||
static int
|
||||
base64_encode (char *out, const byte * in, size_t len, size_t olen)
|
||||
{
|
||||
if (!out || !in)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
while (len >= 3 && olen > 10)
|
||||
{
|
||||
*out++ = b64chars[in[0] >> 2];
|
||||
*out++ = b64chars[((in[0] << 4) & 0x30) | (in[1] >> 4)];
|
||||
*out++ = b64chars[((in[1] << 2) & 0x3c) | (in[2] >> 6)];
|
||||
*out++ = b64chars[in[2] & 0x3f];
|
||||
olen -= 4;
|
||||
len -= 3;
|
||||
in += 3;
|
||||
}
|
||||
|
||||
/* clean up remainder */
|
||||
if (len > 0 && olen > 4)
|
||||
{
|
||||
byte fragment = 0;
|
||||
*out++ = b64chars[in[0] >> 2];
|
||||
fragment = (in[0] << 4) & 0x30;
|
||||
if (len > 1)
|
||||
fragment |= in[1] >> 4;
|
||||
*out++ = b64chars[fragment];
|
||||
*out++ = (len < 2) ? '=' : b64chars[(in[1] << 2) & 0x3c];
|
||||
*out++ = '=';
|
||||
}
|
||||
*out = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Convert '\0'-terminated base64 string to raw byte buffer.
|
||||
Returns length of returned buffer, or -1 on error. */
|
||||
static int
|
||||
base64_decode (byte * out, const char *in)
|
||||
{
|
||||
size_t len;
|
||||
byte digit1, digit2, digit3, digit4;
|
||||
|
||||
if (!out || !in)
|
||||
return -1;
|
||||
|
||||
len = 0;
|
||||
do
|
||||
{
|
||||
digit1 = in[0];
|
||||
if (digit1 > 127 || b64val (digit1) == BAD)
|
||||
return -1;
|
||||
digit2 = in[1];
|
||||
if (digit2 > 127 || b64val (digit2) == BAD)
|
||||
return -1;
|
||||
digit3 = in[2];
|
||||
if (digit3 > 127 || ((digit3 != '=') && (b64val (digit3) == BAD)))
|
||||
return -1;
|
||||
digit4 = in[3];
|
||||
if (digit4 > 127 || ((digit4 != '=') && (b64val (digit4) == BAD)))
|
||||
return -1;
|
||||
in += 4;
|
||||
|
||||
/* digits are already sanity-checked */
|
||||
*out++ = (b64val (digit1) << 2) | (b64val (digit2) >> 4);
|
||||
len++;
|
||||
if (digit3 != '=')
|
||||
{
|
||||
*out++ = ((b64val (digit2) << 4) & 0xf0) | (b64val (digit3) >> 2);
|
||||
len++;
|
||||
if (digit4 != '=')
|
||||
{
|
||||
*out++ = ((b64val (digit3) << 6) & 0xc0) | b64val (digit4);
|
||||
len++;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (*in && digit4 != '=');
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/* Return the compression algorithm in @r_zipalgo.
|
||||
If the parameter is not set after execution,
|
||||
the stream is not compressed. */
|
||||
static int
|
||||
compress_get_algo (cdk_stream_t inp, int *r_zipalgo)
|
||||
{
|
||||
byte plain[512];
|
||||
char buf[128];
|
||||
int nread, pkttype;
|
||||
|
||||
*r_zipalgo = 0;
|
||||
cdk_stream_seek (inp, 0);
|
||||
while (!cdk_stream_eof (inp))
|
||||
{
|
||||
nread = _cdk_stream_gets (inp, buf, DIM (buf) - 1);
|
||||
if (!nread || nread == -1)
|
||||
break;
|
||||
if (nread == 1 && !cdk_stream_eof (inp)
|
||||
&& (nread = _cdk_stream_gets (inp, buf, DIM (buf) - 1)) > 0)
|
||||
{
|
||||
base64_decode (plain, buf);
|
||||
if (!(*plain & 0x80))
|
||||
break;
|
||||
pkttype = *plain & 0x40 ? (*plain & 0x3f) : ((*plain >> 2) & 0xf);
|
||||
if (pkttype == CDK_PKT_COMPRESSED && r_zipalgo)
|
||||
{
|
||||
_cdk_log_debug ("armor compressed (algo=%d)\n", *(plain + 1));
|
||||
*r_zipalgo = *(plain + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
check_armor (cdk_stream_t inp, int *r_zipalgo)
|
||||
{
|
||||
char buf[4096];
|
||||
size_t nread;
|
||||
int check;
|
||||
|
||||
check = 0;
|
||||
nread = cdk_stream_read (inp, buf, DIM (buf) - 1);
|
||||
if (nread > 0)
|
||||
{
|
||||
buf[nread] = '\0';
|
||||
if (strstr (buf, "-----BEGIN PGP"))
|
||||
{
|
||||
compress_get_algo (inp, r_zipalgo);
|
||||
check = 1;
|
||||
}
|
||||
cdk_stream_seek (inp, 0);
|
||||
}
|
||||
return check;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
is_armored (int ctb)
|
||||
{
|
||||
int pkttype = 0;
|
||||
|
||||
if (!(ctb & 0x80))
|
||||
return 1; /* invalid packet: assume it is armored */
|
||||
pkttype = ctb & 0x40 ? (ctb & 0x3f) : ((ctb >> 2) & 0xf);
|
||||
switch (pkttype)
|
||||
{
|
||||
case CDK_PKT_MARKER:
|
||||
case CDK_PKT_SYMKEY_ENC:
|
||||
case CDK_PKT_ONEPASS_SIG:
|
||||
case CDK_PKT_PUBLIC_KEY:
|
||||
case CDK_PKT_SECRET_KEY:
|
||||
case CDK_PKT_PUBKEY_ENC:
|
||||
case CDK_PKT_SIGNATURE:
|
||||
case CDK_PKT_LITERAL:
|
||||
case CDK_PKT_COMPRESSED:
|
||||
case CDK_PKT_ENCRYPTED:
|
||||
return 0; /* seems to be a regular packet: not armored */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static u32
|
||||
update_crc (u32 crc, const byte * buf, size_t buflen)
|
||||
{
|
||||
int j;
|
||||
|
||||
if (!crc)
|
||||
crc = CRCINIT;
|
||||
|
||||
for (j = 0; j < buflen; j++)
|
||||
crc = (crc << 8) ^ crc_table[0xff & ((crc >> 16) ^ buf[j])];
|
||||
crc &= 0xffffff;
|
||||
return crc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
armor_encode (void *opaque, FILE * in, FILE * out)
|
||||
{
|
||||
armor_filter_t *afx = opaque;
|
||||
struct stat statbuf;
|
||||
char crcbuf[5], buf[128], raw[49];
|
||||
byte crcbuf2[3];
|
||||
size_t nread = 0;
|
||||
const char *lf;
|
||||
|
||||
if (!afx)
|
||||
return CDK_Inv_Value;
|
||||
if (afx->idx < 0 || afx->idx > DIM (armor_begin) ||
|
||||
afx->idx2 < 0 || afx->idx2 > DIM (armor_end))
|
||||
return CDK_Inv_Value;
|
||||
|
||||
_cdk_log_debug ("armor filter: encode\n");
|
||||
|
||||
memset (crcbuf, 0, sizeof (crcbuf));
|
||||
|
||||
lf = afx->le ? afx->le : LF;
|
||||
fprintf (out, "-----%s-----%s", armor_begin[afx->idx], lf);
|
||||
fprintf (out, "Version: OpenPrivacy " PACKAGE_VERSION "%s", lf);
|
||||
if (afx->hdrlines)
|
||||
fwrite (afx->hdrlines, 1, strlen (afx->hdrlines), out);
|
||||
fprintf (out, "%s", lf);
|
||||
|
||||
if (fstat (fileno (in), &statbuf))
|
||||
return CDK_General_Error;
|
||||
|
||||
while (!feof (in))
|
||||
{
|
||||
nread = fread (raw, 1, DIM (raw) - 1, in);
|
||||
if (!nread)
|
||||
break;
|
||||
if (ferror (in))
|
||||
return CDK_File_Error;
|
||||
afx->crc = update_crc (afx->crc, (byte *) raw, nread);
|
||||
base64_encode (buf, (byte *) raw, nread, DIM (buf) - 1);
|
||||
fprintf (out, "%s%s", buf, lf);
|
||||
}
|
||||
|
||||
crcbuf2[0] = afx->crc >> 16;
|
||||
crcbuf2[1] = afx->crc >> 8;
|
||||
crcbuf2[2] = afx->crc;
|
||||
crcbuf[0] = b64chars[crcbuf2[0] >> 2];
|
||||
crcbuf[1] = b64chars[((crcbuf2[0] << 4) & 0x30) | (crcbuf2[1] >> 4)];
|
||||
crcbuf[2] = b64chars[((crcbuf2[1] << 2) & 0x3c) | (crcbuf2[2] >> 6)];
|
||||
crcbuf[3] = b64chars[crcbuf2[2] & 0x3f];
|
||||
fprintf (out, "=%s%s", crcbuf, lf);
|
||||
fprintf (out, "-----%s-----%s", armor_end[afx->idx2], lf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_armor_filter_use:
|
||||
* @inp: the stream to check
|
||||
*
|
||||
* Check if the stream contains armored data.
|
||||
**/
|
||||
int
|
||||
cdk_armor_filter_use (cdk_stream_t inp)
|
||||
{
|
||||
int c, check;
|
||||
int zipalgo;
|
||||
|
||||
zipalgo = 0;
|
||||
c = cdk_stream_getc (inp);
|
||||
if (c == EOF)
|
||||
return 0; /* EOF, doesn't matter whether armored or not */
|
||||
cdk_stream_seek (inp, 0);
|
||||
check = is_armored (c);
|
||||
if (check)
|
||||
{
|
||||
check = check_armor (inp, &zipalgo);
|
||||
if (zipalgo)
|
||||
_cdk_stream_set_compress_algo (inp, zipalgo);
|
||||
}
|
||||
return check;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
search_header (const char *buf, const char **array)
|
||||
{
|
||||
const char *s;
|
||||
int i;
|
||||
|
||||
if (strlen (buf) < 5 || strncmp (buf, "-----", 5))
|
||||
return -1;
|
||||
for (i = 0; (s = array[i]); i++)
|
||||
{
|
||||
if (!strncmp (s, buf + 5, strlen (s)))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
_cdk_armor_get_lineend (void)
|
||||
{
|
||||
return LF;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
armor_decode (void *opaque, FILE * in, FILE * out)
|
||||
{
|
||||
armor_filter_t *afx = opaque;
|
||||
const char *s;
|
||||
char buf[127];
|
||||
byte raw[128], crcbuf[4];
|
||||
u32 crc2 = 0;
|
||||
size_t nread = 0;
|
||||
int i, pgp_data = 0;
|
||||
cdk_error_t rc = 0;
|
||||
|
||||
if (!afx)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
_cdk_log_debug ("armor filter: decode\n");
|
||||
|
||||
fseek (in, 0, SEEK_SET);
|
||||
/* Search the begin of the message */
|
||||
while (!feof (in) && !pgp_data)
|
||||
{
|
||||
s = fgets (buf, DIM (buf) - 1, in);
|
||||
if (!s)
|
||||
break;
|
||||
afx->idx = search_header (buf, armor_begin);
|
||||
if (afx->idx >= 0)
|
||||
pgp_data = 1;
|
||||
}
|
||||
|
||||
if (feof (in) || !pgp_data)
|
||||
return CDK_Armor_Error; /* no data found */
|
||||
|
||||
/* Parse header until the empty line is reached */
|
||||
while (!feof (in))
|
||||
{
|
||||
s = fgets (buf, DIM (buf) - 1, in);
|
||||
if (!s)
|
||||
return CDK_EOF;
|
||||
if (strlen (s) == strlen (LF))
|
||||
{
|
||||
rc = 0;
|
||||
break; /* empty line */
|
||||
}
|
||||
/* From RFC2440: OpenPGP should consider improperly formatted Armor
|
||||
Headers to be corruption of the ASCII Armor. A colon and a single
|
||||
space separate the key and value. */
|
||||
if (!strstr (buf, ": "))
|
||||
return CDK_Armor_Error;
|
||||
rc = CDK_General_Error;
|
||||
for (i = 0; (s = valid_headers[i]); i++)
|
||||
{
|
||||
if (!strncmp (s, buf, strlen (s)))
|
||||
rc = 0;
|
||||
}
|
||||
if (rc)
|
||||
{
|
||||
/* From RFC2440: Unknown keys should be reported to the user,
|
||||
but OpenPGP should continue to process the message. */
|
||||
_cdk_log_info ("unknown header: `%s'\n", buf);
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the data body */
|
||||
while (!feof (in))
|
||||
{
|
||||
s = fgets (buf, DIM (buf) - 1, in);
|
||||
if (!s)
|
||||
break;
|
||||
buf[strlen (buf) - strlen (LF)] = '\0';
|
||||
if (buf[0] == '=' && strlen (s) == 5)
|
||||
{ /* CRC */
|
||||
memset (crcbuf, 0, sizeof (crcbuf));
|
||||
base64_decode (crcbuf, buf + 1);
|
||||
crc2 = (crcbuf[0] << 16) | (crcbuf[1] << 8) | crcbuf[2];
|
||||
break; /* stop here */
|
||||
}
|
||||
else
|
||||
{
|
||||
nread = base64_decode (raw, buf);
|
||||
if (nread == -1 || nread == 0)
|
||||
break;
|
||||
afx->crc = update_crc (afx->crc, raw, nread);
|
||||
fwrite (raw, 1, nread, out);
|
||||
}
|
||||
}
|
||||
|
||||
/* Search the tail of the message */
|
||||
s = fgets (buf, DIM (buf) - 1, in);
|
||||
if (s)
|
||||
{
|
||||
buf[strlen (buf) - strlen (LF)] = '\0';
|
||||
rc = CDK_General_Error;
|
||||
afx->idx2 = search_header (buf, armor_end);
|
||||
if (afx->idx2 >= 0)
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
/* This catches error when no tail was found or the header is
|
||||
different then the tail line. */
|
||||
if (rc || afx->idx != afx->idx2)
|
||||
rc = CDK_Armor_Error;
|
||||
|
||||
afx->crc_okay = (afx->crc == crc2) ? 1 : 0;
|
||||
if (!afx->crc_okay && !rc)
|
||||
{
|
||||
_cdk_log_debug ("file crc=%08lX afx_crc=%08lX\n", crc2, afx->crc);
|
||||
rc = CDK_Armor_CRC_Error;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_file_armor:
|
||||
* @hd: Handle
|
||||
* @file: Name of the file to protect.
|
||||
* @output: Output filename.
|
||||
*
|
||||
* Protect a file with ASCII armor.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_file_armor (cdk_ctx_t hd, const char *file, const char *output)
|
||||
{
|
||||
cdk_stream_t inp, out;
|
||||
cdk_error_t rc;
|
||||
|
||||
rc = _cdk_check_args (hd->opt.overwrite, file, output);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = cdk_stream_open (file, &inp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = cdk_stream_new (output, &out);
|
||||
if (rc)
|
||||
{
|
||||
cdk_stream_close (inp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
cdk_stream_set_armor_flag (out, CDK_ARMOR_MESSAGE);
|
||||
if (hd->opt.compress)
|
||||
rc = cdk_stream_set_compress_flag (out, hd->compress.algo,
|
||||
hd->compress.level);
|
||||
if (!rc)
|
||||
rc = cdk_stream_set_literal_flag (out, 0, file);
|
||||
if (!rc)
|
||||
rc = cdk_stream_kick_off (inp, out);
|
||||
if (!rc)
|
||||
rc = _cdk_stream_get_errno (out);
|
||||
|
||||
cdk_stream_close (out);
|
||||
cdk_stream_close (inp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_file_dearmor:
|
||||
* @file: Name of the file to unprotect.
|
||||
* @output: Output filename.
|
||||
*
|
||||
* Remove ASCII armor from a file.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_file_dearmor (const char *file, const char *output)
|
||||
{
|
||||
cdk_stream_t inp, out;
|
||||
cdk_error_t rc;
|
||||
int zipalgo;
|
||||
|
||||
rc = _cdk_check_args (1, file, output);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = cdk_stream_open (file, &inp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = cdk_stream_create (output, &out);
|
||||
if (rc)
|
||||
{
|
||||
cdk_stream_close (inp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (cdk_armor_filter_use (inp))
|
||||
{
|
||||
rc = cdk_stream_set_literal_flag (inp, 0, NULL);
|
||||
zipalgo = cdk_stream_is_compressed (inp);
|
||||
if (zipalgo)
|
||||
rc = cdk_stream_set_compress_flag (inp, zipalgo, 0);
|
||||
if (!rc)
|
||||
rc = cdk_stream_set_armor_flag (inp, 0);
|
||||
if (!rc)
|
||||
rc = cdk_stream_kick_off (inp, out);
|
||||
if (!rc)
|
||||
rc = _cdk_stream_get_errno (inp);
|
||||
}
|
||||
|
||||
cdk_stream_close (inp);
|
||||
cdk_stream_close (out);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
_cdk_filter_armor (void *opaque, int ctl, FILE * in, FILE * out)
|
||||
{
|
||||
if (ctl == STREAMCTL_READ)
|
||||
return armor_decode (opaque, in, out);
|
||||
else if (ctl == STREAMCTL_WRITE)
|
||||
return armor_encode (opaque, in, out);
|
||||
else if (ctl == STREAMCTL_FREE)
|
||||
{
|
||||
armor_filter_t *afx = opaque;
|
||||
if (afx)
|
||||
{
|
||||
_cdk_log_debug ("free armor filter\n");
|
||||
afx->idx = afx->idx2 = 0;
|
||||
afx->crc = afx->crc_okay = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return CDK_Inv_Mode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_armor_encode_buffer:
|
||||
* @inbuf: the raw input buffer
|
||||
* @inlen: raw buffer len
|
||||
* @outbuf: the destination buffer for the base64 output
|
||||
* @outlen: destination buffer len
|
||||
* @nwritten: actual length of the base64 data
|
||||
* @type: the base64 file type.
|
||||
*
|
||||
* Encode the given buffer into base64 format.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_armor_encode_buffer (const byte * inbuf, size_t inlen,
|
||||
char *outbuf, size_t outlen,
|
||||
size_t * nwritten, int type)
|
||||
{
|
||||
const char *head, *tail, *le;
|
||||
byte tempbuf[48];
|
||||
char tempout[128];
|
||||
size_t pos, off, len, rest;
|
||||
|
||||
if (!inbuf || !nwritten)
|
||||
return CDK_Inv_Value;
|
||||
if (type > CDK_ARMOR_SIGNATURE)
|
||||
return CDK_Inv_Mode;
|
||||
|
||||
head = armor_begin[type];
|
||||
tail = armor_end[type];
|
||||
le = _cdk_armor_get_lineend ();
|
||||
pos = strlen (head) + 10 + 2 + 2 + strlen (tail) + 10 + 2 + 5 + 2;
|
||||
/* The output data is 4/3 times larger, plus a line end for each line. */
|
||||
pos += (4 * inlen / 3) + 2 * (4 * inlen / 3 / 64);
|
||||
|
||||
if (outbuf && outlen < pos)
|
||||
return CDK_Too_Short;
|
||||
|
||||
/* Only return the size of the output. */
|
||||
if (!outbuf)
|
||||
{
|
||||
*nwritten = pos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
memset (outbuf, 0, outlen);
|
||||
memcpy (outbuf + pos, "-----", 5);
|
||||
pos += 5;
|
||||
memcpy (outbuf + pos, head, strlen (head));
|
||||
pos += strlen (head);
|
||||
memcpy (outbuf + pos, "-----", 5);
|
||||
pos += 5;
|
||||
memcpy (outbuf + pos, le, strlen (le));
|
||||
pos += strlen (le);
|
||||
memcpy (outbuf + pos, le, strlen (le));
|
||||
pos += strlen (le);
|
||||
rest = inlen;
|
||||
for (off = 0; off < inlen;)
|
||||
{
|
||||
if (rest > 48)
|
||||
{
|
||||
memcpy (tempbuf, inbuf + off, 48);
|
||||
off += 48;
|
||||
len = 48;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy (tempbuf, inbuf + off, rest);
|
||||
off += rest;
|
||||
len = rest;
|
||||
}
|
||||
rest -= len;
|
||||
base64_encode (tempout, tempbuf, len, DIM (tempout) - 1);
|
||||
memcpy (outbuf + pos, tempout, strlen (tempout));
|
||||
pos += strlen (tempout);
|
||||
memcpy (outbuf + pos, le, strlen (le));
|
||||
pos += strlen (le);
|
||||
}
|
||||
|
||||
memcpy (outbuf + pos, "-----", 5);
|
||||
pos += 5;
|
||||
memcpy (outbuf + pos, tail, strlen (tail));
|
||||
pos += strlen (tail);
|
||||
memcpy (outbuf + pos, "-----", 5);
|
||||
pos += 5;
|
||||
memcpy (outbuf + pos, le, strlen (le));
|
||||
pos += strlen (le);
|
||||
*nwritten = pos;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,528 +0,0 @@
|
||||
/* cipher.c - Cipher filters
|
||||
* Copyright (C) 2002, 2003, 2007 Timo Schulz
|
||||
* Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "opencdk.h"
|
||||
#include "main.h"
|
||||
#include "filters.h"
|
||||
|
||||
|
||||
/* The maximal cipher block size in octets. */
|
||||
#define MAX_CIPHER_BLKSIZE 16
|
||||
|
||||
|
||||
static off_t
|
||||
fp_get_length (FILE * fp)
|
||||
{
|
||||
struct stat statbuf;
|
||||
|
||||
if (fstat (fileno (fp), &statbuf))
|
||||
return (off_t) - 1;
|
||||
return statbuf.st_size;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
hash_encode (void *opaque, FILE * in, FILE * out)
|
||||
{
|
||||
md_filter_t *mfx = opaque;
|
||||
byte buf[BUFSIZE];
|
||||
gcry_error_t err;
|
||||
int nread;
|
||||
|
||||
if (!mfx)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
_cdk_log_debug ("hash filter: encode algo=%d\n", mfx->digest_algo);
|
||||
|
||||
if (!mfx->md)
|
||||
{
|
||||
err = gcry_md_open (&mfx->md, mfx->digest_algo, 0);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
}
|
||||
|
||||
while (!feof (in))
|
||||
{
|
||||
nread = fread (buf, 1, BUFSIZE, in);
|
||||
if (!nread)
|
||||
break;
|
||||
gcry_md_write (mfx->md, buf, nread);
|
||||
}
|
||||
|
||||
wipemem (buf, sizeof (buf));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
_cdk_filter_hash (void *opaque, int ctl, FILE * in, FILE * out)
|
||||
{
|
||||
if (ctl == STREAMCTL_READ)
|
||||
return hash_encode (opaque, in, out);
|
||||
else if (ctl == STREAMCTL_FREE)
|
||||
{
|
||||
md_filter_t *mfx = opaque;
|
||||
if (mfx)
|
||||
{
|
||||
_cdk_log_debug ("free hash filter\n");
|
||||
gcry_md_close (mfx->md);
|
||||
mfx->md = NULL;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return CDK_Inv_Mode;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
write_header (cipher_filter_t * cfx, FILE * out)
|
||||
{
|
||||
cdk_pkt_encrypted_t ed;
|
||||
cdk_packet_t pkt;
|
||||
cdk_error_t rc;
|
||||
cdk_dek_t dek = cfx->dek;
|
||||
byte temp[MAX_CIPHER_BLKSIZE + 2];
|
||||
size_t blocksize;
|
||||
int use_mdc, nprefix;
|
||||
gcry_error_t err;
|
||||
|
||||
blocksize = gcry_cipher_get_algo_blklen (dek->algo);
|
||||
if (blocksize < 8 || blocksize > 16)
|
||||
return CDK_Inv_Algo;
|
||||
|
||||
/* It might be possible the receiver does not understand the MDC
|
||||
output and thus we offer to supress the MDC packet. */
|
||||
use_mdc = dek->use_mdc;
|
||||
if (blocksize == 8)
|
||||
use_mdc = 0;
|
||||
|
||||
/* We need to increase the data length because the MDC packet will
|
||||
be also included. It has a fixed length of 22 octets. */
|
||||
if (use_mdc && cfx->datalen)
|
||||
cfx->datalen += 22;
|
||||
|
||||
cdk_pkt_alloc (&pkt, CDK_PKT_ENCRYPTED_MDC);
|
||||
ed = pkt->pkt.encrypted;
|
||||
if (!cfx->blkmode.on)
|
||||
{
|
||||
ed->len = cfx->datalen;
|
||||
ed->extralen = blocksize + 2;
|
||||
}
|
||||
else
|
||||
cfx->blkmode.nleft = DEF_BLOCKSIZE;
|
||||
|
||||
if (use_mdc)
|
||||
{
|
||||
ed->mdc_method = GCRY_MD_SHA1;
|
||||
err = gcry_md_open (&cfx->mdc, GCRY_MD_SHA1, 0);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
}
|
||||
|
||||
/* When we use partial bodies, the MDC feature or a blocksize
|
||||
larger than 8, we force the use of the new packet format. */
|
||||
if (cfx->blkmode.on || use_mdc || blocksize != 8)
|
||||
pkt->old_ctb = 0;
|
||||
else
|
||||
pkt->old_ctb = 1;
|
||||
pkt->pkttype = use_mdc ? CDK_PKT_ENCRYPTED_MDC : CDK_PKT_ENCRYPTED;
|
||||
rc = _cdk_pkt_write_fp (out, pkt);
|
||||
cdk_pkt_release (pkt);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
nprefix = blocksize;
|
||||
gcry_randomize (temp, nprefix, GCRY_STRONG_RANDOM);
|
||||
temp[nprefix] = temp[nprefix - 2];
|
||||
temp[nprefix + 1] = temp[nprefix - 1];
|
||||
err = gcry_cipher_open (&cfx->hd, dek->algo, GCRY_CIPHER_MODE_CFB,
|
||||
use_mdc ? 0 : GCRY_CIPHER_ENABLE_SYNC);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
err = gcry_cipher_setiv (cfx->hd, NULL, 0);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
err = gcry_cipher_setkey (cfx->hd, dek->key, dek->keylen);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
if (cfx->mdc)
|
||||
gcry_md_write (cfx->mdc, temp, nprefix + 2);
|
||||
gcry_cipher_encrypt (cfx->hd, temp, nprefix + 2, NULL, 0);
|
||||
gcry_cipher_sync (cfx->hd);
|
||||
fwrite (temp, 1, nprefix + 2, out);
|
||||
if (cfx->blkmode.on)
|
||||
{
|
||||
cfx->blkmode.nleft -= (nprefix + 2);
|
||||
if (use_mdc)
|
||||
cfx->blkmode.nleft--; /* 1 byte version */
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
write_mdc_packet (FILE * out, cipher_filter_t * cfx)
|
||||
{
|
||||
byte pktdata[22];
|
||||
int dlen = gcry_md_get_algo_dlen (GCRY_MD_SHA1);
|
||||
|
||||
if (!out || !cfx)
|
||||
return CDK_Inv_Value;
|
||||
if (dlen != 20)
|
||||
return CDK_Inv_Algo;
|
||||
|
||||
/* We must hash the prefix of the MDC packet here */
|
||||
pktdata[0] = 0xD3;
|
||||
pktdata[1] = 0x14;
|
||||
gcry_md_write (cfx->mdc, pktdata, 2);
|
||||
gcry_md_final (cfx->mdc);
|
||||
memcpy (pktdata + 2, gcry_md_read (cfx->mdc, GCRY_MD_SHA1), dlen);
|
||||
gcry_cipher_encrypt (cfx->hd, pktdata, dlen + 2, NULL, 0);
|
||||
fwrite (pktdata, 1, dlen + 2, out);
|
||||
wipemem (pktdata, sizeof (pktdata));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static inline int
|
||||
num2bits (size_t n)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; n > 1; i++)
|
||||
n >>= 1;
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
write_partial_block (FILE * in, FILE * out, off_t * r_len,
|
||||
cipher_filter_t * cfx)
|
||||
{
|
||||
gcry_error_t err;
|
||||
byte buf[DEF_BLOCKSIZE];
|
||||
size_t n;
|
||||
int nread;
|
||||
|
||||
if (!out || !cfx)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (!cfx->blkmode.nleft && *r_len > 0)
|
||||
{
|
||||
if (*r_len > DEF_BLOCKSIZE)
|
||||
{
|
||||
/*_cdk_log_debug ("write_partial_block: size %lu block %d\n",
|
||||
*r_len, DEF_BLOCKSIZE);*/
|
||||
fputc ((0xE0 | DEF_BLOCKBITS), out);
|
||||
cfx->blkmode.nleft = DEF_BLOCKSIZE;
|
||||
(*r_len) -= DEF_BLOCKSIZE;
|
||||
}
|
||||
else if (*r_len > 512)
|
||||
{
|
||||
n = num2bits (*r_len);
|
||||
cfx->blkmode.nleft = (1 << n);
|
||||
/*_cdk_log_debug ("write_partial_block: size %lu bits %d block %d\n",
|
||||
*r_len, n, (1<<n));*/
|
||||
fputc ((0xE0 | n), out);
|
||||
(*r_len) -= cfx->blkmode.nleft;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t pktlen = *r_len;
|
||||
|
||||
/* If we use the MDC mode, we need to increase the final
|
||||
partial body length to hold the mdc packet itself. */
|
||||
if (cfx->mdc)
|
||||
pktlen += 22;
|
||||
|
||||
if (pktlen < 192)
|
||||
fputc (pktlen, out);
|
||||
else if (pktlen < 8384)
|
||||
{
|
||||
pktlen -= 192;
|
||||
fputc ((pktlen / 256) + 192, out);
|
||||
fputc ((pktlen % 256), out);
|
||||
}
|
||||
cfx->blkmode.nleft = pktlen;
|
||||
/*_cdk_log_debug ("write_partial_block: end %d block\n", pktlen);*/
|
||||
(*r_len) -= pktlen;
|
||||
}
|
||||
}
|
||||
else
|
||||
(*r_len) -= cfx->blkmode.nleft;
|
||||
|
||||
n = cfx->blkmode.nleft < DIM (buf) ? cfx->blkmode.nleft : DIM (buf);
|
||||
nread = fread (buf, 1, n, in);
|
||||
if (!nread)
|
||||
return CDK_EOF;
|
||||
if (cfx->mdc)
|
||||
gcry_md_write (cfx->mdc, buf, nread);
|
||||
err = gcry_cipher_encrypt (cfx->hd, buf, nread, NULL, 0);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
fwrite (buf, 1, nread, out);
|
||||
cfx->blkmode.nleft -= nread;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
cipher_encode_file (void *opaque, FILE * in, FILE * out)
|
||||
{
|
||||
cipher_filter_t *cfx = opaque;
|
||||
byte buf[BUFSIZE];
|
||||
off_t len, len2;
|
||||
int nread;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!cfx || !in || !out)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
len = len2 = fp_get_length (in);
|
||||
if (len == (off_t) - 1)
|
||||
return CDK_File_Error;
|
||||
while (!feof (in))
|
||||
{
|
||||
if (cfx->blkmode.on)
|
||||
{
|
||||
rc = write_partial_block (in, out, &len2, cfx);
|
||||
if (rc == CDK_EOF)
|
||||
break;
|
||||
if (rc)
|
||||
{
|
||||
wipemem (buf, sizeof (buf));
|
||||
return rc;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
nread = fread (buf, 1, DIM (buf), in);
|
||||
if (!nread)
|
||||
break;
|
||||
if (cfx->mdc)
|
||||
gcry_md_write (cfx->mdc, buf, nread);
|
||||
gcry_cipher_encrypt (cfx->hd, buf, nread, NULL, 0);
|
||||
fwrite (buf, 1, nread, out);
|
||||
}
|
||||
if (cfx->mdc)
|
||||
rc = write_mdc_packet (out, cfx);
|
||||
else
|
||||
rc = 0;
|
||||
|
||||
wipemem (buf, sizeof (buf));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
read_header (cipher_filter_t * cfx, FILE * in)
|
||||
{
|
||||
cdk_dek_t dek;
|
||||
byte temp[32];
|
||||
int blocksize, nprefix;
|
||||
int i, c;
|
||||
gcry_error_t err;
|
||||
|
||||
if (!cfx || !in)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
dek = cfx->dek;
|
||||
blocksize = gcry_cipher_get_algo_blklen (dek->algo);
|
||||
if (blocksize < 8 || blocksize > 16)
|
||||
return CDK_Inv_Algo;
|
||||
|
||||
nprefix = blocksize;
|
||||
if (cfx->datalen > 0 && cfx->datalen < (nprefix + 2))
|
||||
return CDK_Inv_Value;
|
||||
if (cfx->mdc_method)
|
||||
{
|
||||
err = gcry_md_open (&cfx->mdc, cfx->mdc_method, 0);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
}
|
||||
err = gcry_cipher_open (&cfx->hd, dek->algo, GCRY_CIPHER_MODE_CFB,
|
||||
cfx->mdc_method ? 0 : GCRY_CIPHER_ENABLE_SYNC);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
err = gcry_cipher_setiv (cfx->hd, NULL, 0);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
err = gcry_cipher_setkey (cfx->hd, dek->key, dek->keylen);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
|
||||
for (i = 0; i < (nprefix + 2); i++)
|
||||
{
|
||||
c = fgetc (in);
|
||||
if (c == EOF)
|
||||
return CDK_File_Error;
|
||||
temp[i] = c;
|
||||
}
|
||||
gcry_cipher_decrypt (cfx->hd, temp, nprefix + 2, NULL, 0);
|
||||
gcry_cipher_sync (cfx->hd);
|
||||
i = nprefix;
|
||||
if (temp[i - 2] != temp[i] || temp[i - 1] != temp[i + 1])
|
||||
return CDK_Chksum_Error;
|
||||
if (cfx->mdc)
|
||||
gcry_md_write (cfx->mdc, temp, nprefix + 2);
|
||||
if (cfx->blkmode.on)
|
||||
cfx->blkmode.size -= (nprefix + 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
finalize_mdc (gcry_md_hd_t md, const byte * buf, size_t nread)
|
||||
{
|
||||
byte mdcbuf[20];
|
||||
int dlen = gcry_md_get_algo_dlen (GCRY_MD_SHA1);
|
||||
cdk_error_t rc;
|
||||
|
||||
if (dlen != 20)
|
||||
return CDK_Inv_Algo;
|
||||
|
||||
if (buf[nread - dlen - 2] == 0xD3 && buf[nread - dlen - 1] == 0x14)
|
||||
{
|
||||
gcry_md_write (md, buf, nread - dlen);
|
||||
gcry_md_final (md);
|
||||
memcpy (mdcbuf, gcry_md_read (md, GCRY_MD_SHA1), dlen);
|
||||
if (memcmp (mdcbuf, buf + nread - dlen, dlen))
|
||||
rc = CDK_Bad_MDC;
|
||||
else
|
||||
rc = CDK_Success;
|
||||
wipemem (mdcbuf, sizeof (mdcbuf));
|
||||
return rc;
|
||||
}
|
||||
|
||||
return CDK_Inv_Packet;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
cipher_decode_file (void *opaque, FILE * in, FILE * out)
|
||||
{
|
||||
cipher_filter_t *cfx = opaque;
|
||||
cdk_error_t rc;
|
||||
byte buf[BUFSIZE];
|
||||
int nread, nreq;
|
||||
|
||||
if (!cfx || !in || !out)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
while (!feof (in))
|
||||
{
|
||||
/*_cdk_log_debug ("partial on=%d size=%lu\n",
|
||||
cfx->blkmode.on, cfx->blkmode.size);*/
|
||||
nreq = cfx->blkmode.on ? cfx->blkmode.size : DIM (buf);
|
||||
nread = fread (buf, 1, nreq, in);
|
||||
if (!nread)
|
||||
break;
|
||||
gcry_cipher_decrypt (cfx->hd, buf, nread, NULL, 0);
|
||||
if (feof (in) && cfx->mdc)
|
||||
{
|
||||
rc = finalize_mdc (cfx->mdc, buf, nread);
|
||||
if (rc)
|
||||
{
|
||||
wipemem (buf, sizeof (buf));
|
||||
return rc;
|
||||
}
|
||||
/* We need to correct the size here to avoid the MDC
|
||||
packet will be written to the output. */
|
||||
nread -= 22;
|
||||
}
|
||||
else if (cfx->mdc)
|
||||
gcry_md_write (cfx->mdc, buf, nread);
|
||||
fwrite (buf, 1, nread, out);
|
||||
if (cfx->blkmode.on)
|
||||
{
|
||||
cfx->blkmode.size = _cdk_pkt_read_len (in, &cfx->blkmode.on);
|
||||
if (cfx->blkmode.size == (size_t) EOF)
|
||||
return CDK_Inv_Packet;
|
||||
}
|
||||
}
|
||||
|
||||
wipemem (buf, sizeof (buf));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
cipher_decode (void *opaque, FILE * in, FILE * out)
|
||||
{
|
||||
cipher_filter_t *cfx = opaque;
|
||||
cdk_error_t rc;
|
||||
|
||||
_cdk_log_debug ("cipher filter: decode\n");
|
||||
|
||||
if (!cfx || !in || !out)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
rc = read_header (cfx, in);
|
||||
if (!rc)
|
||||
rc = cipher_decode_file (cfx, in, out);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
cipher_encode (void *opaque, FILE * in, FILE * out)
|
||||
{
|
||||
cipher_filter_t *cfx = opaque;
|
||||
cdk_error_t rc;
|
||||
|
||||
_cdk_log_debug ("cipher filter: encode\n");
|
||||
|
||||
if (!cfx || !in || !out)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
cfx->datalen = fp_get_length (in);
|
||||
if (cfx->datalen < BUFSIZE && cfx->blkmode.on)
|
||||
cfx->blkmode.on = 0;
|
||||
rc = write_header (cfx, out);
|
||||
if (!rc)
|
||||
rc = cipher_encode_file (cfx, in, out);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
_cdk_filter_cipher (void *opaque, int ctl, FILE * in, FILE * out)
|
||||
{
|
||||
if (ctl == STREAMCTL_READ)
|
||||
return cipher_decode (opaque, in, out);
|
||||
else if (ctl == STREAMCTL_WRITE)
|
||||
return cipher_encode (opaque, in, out);
|
||||
else if (ctl == STREAMCTL_FREE)
|
||||
{
|
||||
cipher_filter_t *cfx = opaque;
|
||||
if (cfx)
|
||||
{
|
||||
_cdk_log_debug ("free cipher filter\n");
|
||||
gcry_md_close (cfx->mdc);
|
||||
cfx->mdc = NULL;
|
||||
gcry_cipher_close (cfx->hd);
|
||||
cfx->hd = NULL;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return CDK_Inv_Mode;
|
||||
}
|
||||
@@ -1,238 +0,0 @@
|
||||
/* compress.c - Compression filters
|
||||
* Copyright (C) 2002, 2003 Timo Schulz
|
||||
* Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#ifdef HAVE_LIBZ
|
||||
# include <zlib.h>
|
||||
#endif
|
||||
|
||||
#include "opencdk.h"
|
||||
#include "main.h"
|
||||
#include "filters.h"
|
||||
|
||||
#ifdef HAVE_LIBZ
|
||||
static int
|
||||
compress_data (z_stream * zs, int flush, byte * inbuf, size_t insize,
|
||||
FILE * out)
|
||||
{
|
||||
int nbytes, zrc;
|
||||
byte buf[4096];
|
||||
|
||||
zs->next_in = inbuf;
|
||||
zs->avail_in = insize;
|
||||
|
||||
do
|
||||
{
|
||||
zs->next_out = buf;
|
||||
zs->avail_out = DIM (buf);
|
||||
|
||||
zrc = deflate (zs, flush);
|
||||
if (zrc == Z_STREAM_END && flush == Z_FINISH)
|
||||
;
|
||||
else if (zrc != Z_OK)
|
||||
break;
|
||||
nbytes = DIM (buf) - zs->avail_out;
|
||||
fwrite (buf, 1, nbytes, out);
|
||||
}
|
||||
while (zs->avail_out == 0 || (flush == Z_FINISH && zrc != Z_STREAM_END));
|
||||
return zrc;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
decompress_data (compress_filter_t * zfx, z_stream * zs,
|
||||
FILE * in, size_t * ret_len)
|
||||
{
|
||||
int nread, nold;
|
||||
int rc, zrc;
|
||||
|
||||
rc = 0;
|
||||
nread = 0;
|
||||
while (zs->avail_out != 0)
|
||||
{
|
||||
if (!zs->avail_in)
|
||||
{
|
||||
nread = fread (zfx->inbuf, 1, zfx->inbufsize, in);
|
||||
zs->next_in = zfx->inbuf;
|
||||
zs->avail_in = nread;
|
||||
}
|
||||
nold = zs->avail_out;
|
||||
zrc = inflate (zs, Z_SYNC_FLUSH);
|
||||
if (zrc != Z_OK && zrc != Z_STREAM_END)
|
||||
{
|
||||
rc = CDK_Zlib_Error;
|
||||
break;
|
||||
}
|
||||
*ret_len = zfx->outbufsize - zs->avail_out;
|
||||
if (nold == zs->avail_out)
|
||||
break;
|
||||
if (zrc == Z_STREAM_END)
|
||||
{
|
||||
rc = EOF; /* eof */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!nread && feof (in))
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
compress_decode (void *opaque, FILE * in, FILE * out)
|
||||
{
|
||||
compress_filter_t *zfx = opaque;
|
||||
z_stream *zs;
|
||||
size_t nbytes;
|
||||
int zrc;
|
||||
cdk_error_t rc = 0;
|
||||
|
||||
_cdk_log_debug ("compress filter: decode (algo=%d)\n", zfx->algo);
|
||||
|
||||
if (!zfx || !in || !out)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
zs = cdk_calloc (1, sizeof *zs);
|
||||
if (!zs)
|
||||
return CDK_Out_Of_Core;
|
||||
if (zfx->algo == CDK_COMPRESS_ZIP)
|
||||
zrc = inflateInit2 (zs, -13);
|
||||
else
|
||||
zrc = inflateInit (zs);
|
||||
if (zrc != Z_OK)
|
||||
return CDK_Zlib_Error;
|
||||
|
||||
zfx->outbufsize = 8192;
|
||||
zfx->inbufsize = 2048;
|
||||
memset (zfx->inbuf, 0, sizeof zfx->inbuf);
|
||||
zs->avail_in = 0;
|
||||
|
||||
nbytes = 0;
|
||||
while (rc != -1)
|
||||
{
|
||||
zs->next_out = zfx->outbuf;
|
||||
zs->avail_out = 8192;
|
||||
rc = decompress_data (zfx, zs, in, &nbytes);
|
||||
fwrite (zfx->outbuf, 1, nbytes, out);
|
||||
}
|
||||
inflateEnd (zs);
|
||||
cdk_free (zs);
|
||||
if (rc == CDK_EOF)
|
||||
rc = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
compress_encode (void *opaque, FILE * in, FILE * out)
|
||||
{
|
||||
compress_filter_t *zfx = opaque;
|
||||
z_stream *zs;
|
||||
struct cdk_pkt_compressed_s cd;
|
||||
struct cdk_packet_s pkt;
|
||||
int zrc, nread;
|
||||
cdk_error_t rc;
|
||||
|
||||
_cdk_log_debug ("compress filter: encode\n");
|
||||
|
||||
if (!zfx || !in || !out)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (!zfx->algo)
|
||||
zfx->algo = CDK_COMPRESS_ZIP;
|
||||
|
||||
memset (&cd, 0, sizeof (cd));
|
||||
cd.len = 0;
|
||||
cd.algorithm = zfx->algo;
|
||||
pkt.pkttype = CDK_PKT_COMPRESSED;
|
||||
pkt.pkt.compressed = &cd;
|
||||
rc = _cdk_pkt_write_fp (out, &pkt);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
zs = cdk_calloc (1, sizeof *zs);
|
||||
if (!zs)
|
||||
return CDK_Out_Of_Core;
|
||||
if (zfx->algo == CDK_COMPRESS_ZIP)
|
||||
rc = deflateInit2 (zs, zfx->level, Z_DEFLATED, -13, 8,
|
||||
Z_DEFAULT_STRATEGY);
|
||||
else
|
||||
rc = deflateInit (zs, zfx->level);
|
||||
if (rc != Z_OK)
|
||||
{
|
||||
cdk_free (zs);
|
||||
return CDK_Zlib_Error;
|
||||
}
|
||||
zfx->outbufsize = 8192;
|
||||
memset (zfx->outbuf, 0, sizeof zfx->outbuf);
|
||||
|
||||
while (!feof (in))
|
||||
{
|
||||
nread = fread (zfx->outbuf, 1, zfx->outbufsize, in);
|
||||
if (!nread)
|
||||
break;
|
||||
zrc = compress_data (zs, Z_NO_FLUSH, zfx->outbuf, nread, out);
|
||||
if (zrc)
|
||||
{
|
||||
rc = CDK_Zlib_Error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!rc)
|
||||
{
|
||||
nread = 0;
|
||||
zrc = compress_data (zs, Z_FINISH, zfx->outbuf, nread, out);
|
||||
if (zrc != Z_STREAM_END)
|
||||
rc = CDK_Zlib_Error;
|
||||
}
|
||||
deflateEnd (zs);
|
||||
cdk_free (zs);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
_cdk_filter_compress (void *opaque, int ctl, FILE * in, FILE * out)
|
||||
{
|
||||
if (ctl == STREAMCTL_READ)
|
||||
return compress_decode (opaque, in, out);
|
||||
else if (ctl == STREAMCTL_WRITE)
|
||||
return compress_encode (opaque, in, out);
|
||||
else if (ctl == STREAMCTL_FREE)
|
||||
{
|
||||
compress_filter_t *zfx = opaque;
|
||||
if (zfx)
|
||||
{
|
||||
_cdk_log_debug ("free compress filter\n");
|
||||
zfx->level = 0;
|
||||
zfx->algo = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return CDK_Inv_Mode;
|
||||
}
|
||||
|
||||
#else
|
||||
cdk_error_t
|
||||
_cdk_filter_compress (void *opaque, int ctl, FILE * in, FILE * out)
|
||||
{
|
||||
return CDK_Not_Implemented;
|
||||
}
|
||||
#endif /* HAVE_LIBZ */
|
||||
@@ -1,120 +0,0 @@
|
||||
/* context.h
|
||||
* Copyright (C) 2002, 2003, 2007 Timo Schulz
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifndef CDK_CONTEXT_H
|
||||
#define CDK_CONTEXT_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
struct cdk_listkey_s {
|
||||
unsigned init:1;
|
||||
cdk_stream_t inp;
|
||||
cdk_keydb_hd_t db;
|
||||
int type;
|
||||
union {
|
||||
char *patt;
|
||||
cdk_strlist_t fpatt;
|
||||
} u;
|
||||
cdk_strlist_t t;
|
||||
};
|
||||
|
||||
|
||||
struct cdk_s2k_s {
|
||||
int mode;
|
||||
byte hash_algo;
|
||||
byte salt[8];
|
||||
u32 count;
|
||||
};
|
||||
|
||||
|
||||
struct cdk_ctx_s {
|
||||
int cipher_algo;
|
||||
int digest_algo;
|
||||
struct {
|
||||
int algo;
|
||||
int level;
|
||||
} compress;
|
||||
struct {
|
||||
int mode;
|
||||
int digest_algo;
|
||||
} _s2k;
|
||||
struct {
|
||||
unsigned blockmode:1;
|
||||
unsigned armor:1;
|
||||
unsigned textmode:1;
|
||||
unsigned compress:1;
|
||||
unsigned mdc:1;
|
||||
unsigned overwrite;
|
||||
unsigned force_digest:1;
|
||||
} opt;
|
||||
struct {
|
||||
cdk_verify_result_t verify;
|
||||
} result;
|
||||
struct {
|
||||
cdk_pkt_seckey_t sk;
|
||||
unsigned on:1;
|
||||
} cache;
|
||||
cdk_dek_t dek;
|
||||
struct {
|
||||
cdk_keydb_hd_t sec;
|
||||
cdk_keydb_hd_t pub;
|
||||
unsigned int close_db:1;
|
||||
} db;
|
||||
char *(*passphrase_cb) (void *opaque, const char *prompt);
|
||||
void * passphrase_cb_value;
|
||||
};
|
||||
|
||||
struct cdk_prefitem_s {
|
||||
byte type;
|
||||
byte value;
|
||||
};
|
||||
|
||||
struct cdk_desig_revoker_s {
|
||||
struct cdk_desig_revoker_s * next;
|
||||
byte r_class;
|
||||
byte algid;
|
||||
byte fpr[KEY_FPR_LEN];
|
||||
};
|
||||
|
||||
struct cdk_subpkt_s {
|
||||
struct cdk_subpkt_s * next;
|
||||
u32 size;
|
||||
byte type;
|
||||
byte d[1];
|
||||
};
|
||||
|
||||
struct cdk_keylist_s {
|
||||
struct cdk_keylist_s * next;
|
||||
union {
|
||||
cdk_pkt_pubkey_t pk;
|
||||
cdk_pkt_seckey_t sk;
|
||||
} key;
|
||||
int version;
|
||||
int type;
|
||||
};
|
||||
|
||||
struct cdk_dek_s {
|
||||
int algo;
|
||||
int keylen;
|
||||
int use_mdc;
|
||||
byte key[32]; /* 256-bit */
|
||||
};
|
||||
|
||||
struct cdk_strlist_s {
|
||||
struct cdk_strlist_s * next;
|
||||
char d[1];
|
||||
};
|
||||
|
||||
#endif /* CDK_CONTEXT_H */
|
||||
@@ -1,15 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "opencdk.h"
|
||||
#include "main.h"
|
||||
#include "filters.h"
|
||||
#include "packet.h"
|
||||
|
||||
cdk_error_t
|
||||
_cdk_proc_packets (cdk_ctx_t hd, cdk_stream_t inp, cdk_stream_t data,
|
||||
const char *output, cdk_stream_t outstream,
|
||||
gcry_md_hd_t md)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/* filters.h - Filter structs
|
||||
* Copyright (C) 2002, 2003 Timo Schulz
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifndef CDK_FILTERS_H
|
||||
#define CDK_FILTERS_H
|
||||
|
||||
enum {
|
||||
STREAMCTL_READ = 0,
|
||||
STREAMCTL_WRITE = 1,
|
||||
STREAMCTL_FREE = 2
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
gcry_cipher_hd_t hd;
|
||||
gcry_md_hd_t mdc;
|
||||
int mdc_method;
|
||||
cdk_dek_t dek;
|
||||
u32 datalen;
|
||||
struct {
|
||||
size_t on;
|
||||
off_t size;
|
||||
off_t nleft;
|
||||
} blkmode;
|
||||
cdk_stream_t s;
|
||||
} cipher_filter_t;
|
||||
|
||||
typedef struct {
|
||||
int digest_algo;
|
||||
gcry_md_hd_t md;
|
||||
} md_filter_t;
|
||||
|
||||
typedef struct {
|
||||
const char *le; /* line endings */
|
||||
const char *hdrlines;
|
||||
u32 crc;
|
||||
int crc_okay;
|
||||
int idx, idx2;
|
||||
} armor_filter_t;
|
||||
|
||||
typedef struct {
|
||||
cdk_lit_format_t mode;
|
||||
char *orig_filename; /* This original name of the input file. */
|
||||
char *filename;
|
||||
gcry_md_hd_t md;
|
||||
struct {
|
||||
size_t on;
|
||||
off_t size;
|
||||
} blkmode;
|
||||
} literal_filter_t;
|
||||
|
||||
typedef struct {
|
||||
size_t inbufsize;
|
||||
byte inbuf[8192];
|
||||
size_t outbufsize;
|
||||
byte outbuf[8192];
|
||||
int algo; /* compress algo */
|
||||
int level;
|
||||
} compress_filter_t;
|
||||
|
||||
typedef struct {
|
||||
const char * lf;
|
||||
} text_filter_t;
|
||||
|
||||
|
||||
/*-- armor.c -*/
|
||||
int _cdk_filter_armor( void * opaque, int ctl, FILE * in, FILE * out );
|
||||
|
||||
/*-- cipher.c --*/
|
||||
cdk_error_t _cdk_filter_hash( void * opaque, int ctl, FILE * in, FILE * out );
|
||||
cdk_error_t _cdk_filter_cipher( void * opaque, int ctl,
|
||||
FILE * in, FILE * out );
|
||||
|
||||
/*-- literal.c --*/
|
||||
int _cdk_filter_literal( void * opaque, int ctl, FILE * in, FILE * out );
|
||||
int _cdk_filter_text( void * opaque, int ctl, FILE * in, FILE * out );
|
||||
|
||||
/*-- compress.c --*/
|
||||
cdk_error_t _cdk_filter_compress( void * opaque, int ctl,
|
||||
FILE * in, FILE * out );
|
||||
|
||||
#endif /* CDK_FILTERS_H */
|
||||
|
||||
|
||||
@@ -1,581 +0,0 @@
|
||||
/* kbnode.c - keyblock node utility functions
|
||||
* Copyright (C) 1998-2001 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2002, 2003, 2007 Timo Schulz
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "opencdk.h"
|
||||
#include "main.h"
|
||||
#include "packet.h"
|
||||
|
||||
|
||||
/**
|
||||
* cdk_kbnode_new:
|
||||
* @pkt: the packet to add
|
||||
*
|
||||
* Allocate a new key node and add the packet.
|
||||
**/
|
||||
cdk_kbnode_t
|
||||
cdk_kbnode_new (cdk_packet_t pkt)
|
||||
{
|
||||
cdk_kbnode_t n;
|
||||
|
||||
n = cdk_calloc (1, sizeof *n);
|
||||
if (!n)
|
||||
return NULL;
|
||||
n->pkt = pkt;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_cdk_kbnode_clone (cdk_kbnode_t node)
|
||||
{
|
||||
/* Mark the node as clone which means that the packet
|
||||
will not be freed, just the node itself. */
|
||||
if (node)
|
||||
node->is_cloned = 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_kbnode_release:
|
||||
* @n: the key node
|
||||
*
|
||||
* Release the memory of the node.
|
||||
**/
|
||||
void
|
||||
cdk_kbnode_release (cdk_kbnode_t node)
|
||||
{
|
||||
cdk_kbnode_t n2;
|
||||
|
||||
while (node)
|
||||
{
|
||||
n2 = node->next;
|
||||
if (!node->is_cloned)
|
||||
cdk_pkt_release (node->pkt);
|
||||
cdk_free (node);
|
||||
node = n2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_kbnode_delete:
|
||||
* @node: the key node
|
||||
*
|
||||
* Mark the given node as deleted.
|
||||
**/
|
||||
void
|
||||
cdk_kbnode_delete (cdk_kbnode_t node)
|
||||
{
|
||||
if (node)
|
||||
node->is_deleted = 1;
|
||||
}
|
||||
|
||||
|
||||
/* Append NODE to ROOT. ROOT must exist! */
|
||||
void
|
||||
_cdk_kbnode_add (cdk_kbnode_t root, cdk_kbnode_t node)
|
||||
{
|
||||
cdk_kbnode_t n1;
|
||||
|
||||
for (n1 = root; n1->next; n1 = n1->next)
|
||||
;
|
||||
n1->next = node;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_kbnode_insert:
|
||||
* @root: the root key node
|
||||
* @node: the node to add
|
||||
* @pkttype: packet type
|
||||
*
|
||||
* Insert @node into the list after @root but before a packet which is not of
|
||||
* type @pkttype (only if @pkttype != 0).
|
||||
**/
|
||||
void
|
||||
cdk_kbnode_insert (cdk_kbnode_t root, cdk_kbnode_t node, int pkttype)
|
||||
{
|
||||
if (!pkttype)
|
||||
{
|
||||
node->next = root->next;
|
||||
root->next = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
cdk_kbnode_t n1;
|
||||
|
||||
for (n1 = root; n1->next; n1 = n1->next)
|
||||
if (pkttype != n1->next->pkt->pkttype)
|
||||
{
|
||||
node->next = n1->next;
|
||||
n1->next = node;
|
||||
return;
|
||||
}
|
||||
/* No such packet, append */
|
||||
node->next = NULL;
|
||||
n1->next = node;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_kbnode_find_prev:
|
||||
* @root: the root key node
|
||||
* @node: the key node
|
||||
* @pkttype: packet type
|
||||
*
|
||||
* Find the previous node (if @pkttype = 0) or the previous node
|
||||
* with pkttype @pkttype in the list starting with @root of @node.
|
||||
**/
|
||||
cdk_kbnode_t
|
||||
cdk_kbnode_find_prev (cdk_kbnode_t root, cdk_kbnode_t node, int pkttype)
|
||||
{
|
||||
cdk_kbnode_t n1;
|
||||
|
||||
for (n1 = NULL; root && root != node; root = root->next)
|
||||
{
|
||||
if (!pkttype || root->pkt->pkttype == pkttype)
|
||||
n1 = root;
|
||||
}
|
||||
return n1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_kbnode_find_next:
|
||||
* @node: the key node
|
||||
* @pkttype: packet type
|
||||
*
|
||||
* Ditto, but find the next packet. The behaviour is trivial if
|
||||
* @pkttype is 0 but if it is specified, the next node with a packet
|
||||
* of this type is returned. The function has some knowledge about
|
||||
* the valid ordering of packets: e.g. if the next signature packet
|
||||
* is requested, the function will not return one if it encounters
|
||||
* a user-id.
|
||||
**/
|
||||
cdk_kbnode_t
|
||||
cdk_kbnode_find_next (cdk_kbnode_t node, int pkttype)
|
||||
{
|
||||
for (node = node->next; node; node = node->next)
|
||||
{
|
||||
if (!pkttype)
|
||||
return node;
|
||||
else if (pkttype == CDK_PKT_USER_ID &&
|
||||
(node->pkt->pkttype == CDK_PKT_PUBLIC_KEY ||
|
||||
node->pkt->pkttype == CDK_PKT_SECRET_KEY))
|
||||
return NULL;
|
||||
else if (pkttype == CDK_PKT_SIGNATURE &&
|
||||
(node->pkt->pkttype == CDK_PKT_USER_ID ||
|
||||
node->pkt->pkttype == CDK_PKT_PUBLIC_KEY ||
|
||||
node->pkt->pkttype == CDK_PKT_SECRET_KEY))
|
||||
return NULL;
|
||||
else if (node->pkt->pkttype == pkttype)
|
||||
return node;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_kbnode_find:
|
||||
* @node: the key node
|
||||
* @pkttype: packet type
|
||||
*
|
||||
* Try to find the next node with the packettype @pkttype.
|
||||
**/
|
||||
cdk_kbnode_t
|
||||
cdk_kbnode_find (cdk_kbnode_t node, int pkttype)
|
||||
{
|
||||
for (; node; node = node->next)
|
||||
{
|
||||
if (node->pkt->pkttype == pkttype)
|
||||
return node;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_kbnode_find_packet:
|
||||
* @node: the key node
|
||||
* @pkttype: packet type
|
||||
*
|
||||
* Same as cdk_kbnode_find but it returns the packet instead of the node.
|
||||
**/
|
||||
cdk_packet_t
|
||||
cdk_kbnode_find_packet (cdk_kbnode_t node, int pkttype)
|
||||
{
|
||||
cdk_kbnode_t res;
|
||||
|
||||
res = cdk_kbnode_find (node, pkttype);
|
||||
return res ? res->pkt : NULL;
|
||||
}
|
||||
|
||||
|
||||
/****************
|
||||
* Walk through a list of kbnodes. This function returns
|
||||
* the next kbnode for each call; before using the function the first
|
||||
* time, the caller must set CONTEXT to NULL (This has simply the effect
|
||||
* to start with ROOT).
|
||||
*/
|
||||
cdk_kbnode_t
|
||||
cdk_kbnode_walk (cdk_kbnode_t root, cdk_kbnode_t * context, int all)
|
||||
{
|
||||
cdk_kbnode_t n;
|
||||
|
||||
do
|
||||
{
|
||||
if (!*context)
|
||||
{
|
||||
*context = root;
|
||||
n = root;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = (*context)->next;
|
||||
*context = n;
|
||||
}
|
||||
}
|
||||
while (!all && n && n->is_deleted);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_kbnode_commit:
|
||||
* @root: the nodes
|
||||
*
|
||||
* Commit changes made to the kblist at ROOT. Note that ROOT my change,
|
||||
* and it is therefore passed by reference.
|
||||
* The function has the effect of removing all nodes marked as deleted.
|
||||
* returns true if any node has been changed
|
||||
*/
|
||||
int
|
||||
cdk_kbnode_commit (cdk_kbnode_t * root)
|
||||
{
|
||||
cdk_kbnode_t n, nl;
|
||||
int changed = 0;
|
||||
|
||||
for (n = *root, nl = NULL; n; n = nl->next)
|
||||
{
|
||||
if (n->is_deleted)
|
||||
{
|
||||
if (n == *root)
|
||||
*root = nl = n->next;
|
||||
else
|
||||
nl->next = n->next;
|
||||
if (!n->is_cloned)
|
||||
cdk_pkt_release (n->pkt);
|
||||
cdk_free (n);
|
||||
changed = 1;
|
||||
}
|
||||
else
|
||||
nl = n;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_kbnode_remove:
|
||||
* @root: the root node
|
||||
* @node: the node to delete
|
||||
*
|
||||
* Remove a node from the root node.
|
||||
*/
|
||||
void
|
||||
cdk_kbnode_remove (cdk_kbnode_t * root, cdk_kbnode_t node)
|
||||
{
|
||||
cdk_kbnode_t n, nl;
|
||||
|
||||
for (n = *root, nl = NULL; n; n = nl->next)
|
||||
{
|
||||
if (n == node)
|
||||
{
|
||||
if (n == *root)
|
||||
*root = nl = n->next;
|
||||
else
|
||||
nl->next = n->next;
|
||||
if (!n->is_cloned)
|
||||
cdk_pkt_release (n->pkt);
|
||||
cdk_free (n);
|
||||
}
|
||||
else
|
||||
nl = n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* cdk_cdknode_move:
|
||||
* @root: root node
|
||||
* @node: the node to move
|
||||
* @where: destination place where to move the node.
|
||||
*
|
||||
* Move NODE behind right after WHERE or to the beginning if WHERE is NULL.
|
||||
*/
|
||||
void
|
||||
cdk_kbnode_move (cdk_kbnode_t * root, cdk_kbnode_t node, cdk_kbnode_t where)
|
||||
{
|
||||
cdk_kbnode_t tmp, prev;
|
||||
|
||||
if (!root || !*root || !node)
|
||||
return;
|
||||
for (prev = *root; prev && prev->next != node; prev = prev->next)
|
||||
;
|
||||
if (!prev)
|
||||
return; /* Node is not in the list */
|
||||
|
||||
if (!where)
|
||||
{ /* Move node before root */
|
||||
if (node == *root)
|
||||
return;
|
||||
prev->next = node->next;
|
||||
node->next = *root;
|
||||
*root = node;
|
||||
return;
|
||||
}
|
||||
if (node == where) /* Move it after where. */
|
||||
return;
|
||||
tmp = node->next;
|
||||
node->next = where->next;
|
||||
where->next = node;
|
||||
prev->next = tmp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_kbnode_get_packet:
|
||||
* @node: the key node
|
||||
*
|
||||
* Return the packet which is stored inside the node in @node.
|
||||
**/
|
||||
cdk_packet_t
|
||||
cdk_kbnode_get_packet (cdk_kbnode_t node)
|
||||
{
|
||||
if (node)
|
||||
return node->pkt;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_kbnode_read_from_mem:
|
||||
* @ret_node: the new key node
|
||||
* @buf: the buffer which stores the key sequence
|
||||
* @buflen: the length of the buffer
|
||||
*
|
||||
* Try to read a key node from the memory buffer @buf.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_kbnode_read_from_mem (cdk_kbnode_t * ret_node,
|
||||
const byte * buf, size_t buflen)
|
||||
{
|
||||
cdk_stream_t inp;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!buflen || !ret_node || !buf)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
*ret_node = NULL;
|
||||
rc = cdk_stream_tmp_from_mem (buf, buflen, &inp);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = cdk_keydb_get_keyblock (inp, ret_node);
|
||||
cdk_stream_close (inp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdk_kbnode_write_to_mem_alloc:
|
||||
* @node: the key node
|
||||
* @r_buf: buffer to hold the raw data
|
||||
* @r_buflen: buffer length of the allocated raw data.
|
||||
*
|
||||
* The function acts similar to cdk_kbnode_write_to_mem but
|
||||
* it allocates the buffer to avoid the lengthy second run.
|
||||
*/
|
||||
cdk_error_t
|
||||
cdk_kbnode_write_to_mem_alloc (cdk_kbnode_t node,
|
||||
byte ** r_buf, size_t * r_buflen)
|
||||
{
|
||||
cdk_kbnode_t n;
|
||||
cdk_stream_t s;
|
||||
cdk_error_t rc;
|
||||
size_t len;
|
||||
|
||||
if (!node)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
*r_buf = NULL;
|
||||
*r_buflen = 0;
|
||||
|
||||
rc = cdk_stream_tmp_new (&s);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
for (n = node; n; n = n->next)
|
||||
{
|
||||
/* Skip all packets which cannot occur in a key composition. */
|
||||
if (n->pkt->pkttype != CDK_PKT_PUBLIC_KEY &&
|
||||
n->pkt->pkttype != CDK_PKT_PUBLIC_SUBKEY &&
|
||||
n->pkt->pkttype != CDK_PKT_SECRET_KEY &&
|
||||
n->pkt->pkttype != CDK_PKT_SECRET_SUBKEY &&
|
||||
n->pkt->pkttype != CDK_PKT_SIGNATURE &&
|
||||
n->pkt->pkttype != CDK_PKT_USER_ID &&
|
||||
n->pkt->pkttype != CDK_PKT_ATTRIBUTE)
|
||||
continue;
|
||||
rc = cdk_pkt_write (s, n->pkt);
|
||||
if (rc)
|
||||
{
|
||||
cdk_stream_close (s);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
cdk_stream_seek (s, 0);
|
||||
len = cdk_stream_get_length (s);
|
||||
*r_buf = cdk_calloc (1, len);
|
||||
*r_buflen = cdk_stream_read (s, *r_buf, len);
|
||||
cdk_stream_close (s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_kbnode_write_to_mem:
|
||||
* @node: the key node
|
||||
* @buf: the buffer to store the node data
|
||||
* @r_nbytes: the new length of the buffer.
|
||||
*
|
||||
* Try to write the contents of the key node to the buffer @buf and
|
||||
* return the length of it in @r_nbytes. If buf is zero, only the
|
||||
* length of the node is calculated and returned in @r_nbytes.
|
||||
* Whenever it is possible, the cdk_kbnode_write_to_mem_alloc should be used.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_kbnode_write_to_mem (cdk_kbnode_t node, byte * buf, size_t * r_nbytes)
|
||||
{
|
||||
cdk_kbnode_t n;
|
||||
cdk_stream_t s;
|
||||
size_t len;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!node)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
rc = cdk_stream_tmp_new (&s);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
for (n = node; n; n = n->next)
|
||||
{
|
||||
/* Skip all packets which cannot occur in a key composition. */
|
||||
if (n->pkt->pkttype != CDK_PKT_PUBLIC_KEY &&
|
||||
n->pkt->pkttype != CDK_PKT_PUBLIC_SUBKEY &&
|
||||
n->pkt->pkttype != CDK_PKT_SECRET_KEY &&
|
||||
n->pkt->pkttype != CDK_PKT_SECRET_SUBKEY &&
|
||||
n->pkt->pkttype != CDK_PKT_SIGNATURE &&
|
||||
n->pkt->pkttype != CDK_PKT_USER_ID &&
|
||||
n->pkt->pkttype != CDK_PKT_ATTRIBUTE)
|
||||
continue;
|
||||
rc = cdk_pkt_write (s, n->pkt);
|
||||
if (rc)
|
||||
{
|
||||
cdk_stream_close (s);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
cdk_stream_seek (s, 0);
|
||||
len = cdk_stream_get_length (s);
|
||||
if (!buf)
|
||||
{
|
||||
*r_nbytes = len; /* Only return the length of the buffer */
|
||||
cdk_stream_close (s);
|
||||
return 0;
|
||||
}
|
||||
if (*r_nbytes < len)
|
||||
rc = CDK_Too_Short;
|
||||
if (!rc)
|
||||
*r_nbytes = cdk_stream_read (s, buf, len);
|
||||
cdk_stream_close (s);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_kbnode_hash:
|
||||
* @node: the key node
|
||||
* @hashctx: opaque pointer to the hash context
|
||||
* @is_v4: OpenPGP signature (yes=1, no=0)
|
||||
* @pkttype: packet type to hash (if zero use the packet type from the node)
|
||||
* @flags: flags which depend on the operation
|
||||
*
|
||||
* Hash the key node contents. Two modes are supported. If the packet
|
||||
* type is used (!= 0) then the function searches the first node with
|
||||
* this type. Otherwise the node is seen as a single node and the type
|
||||
* is extracted from it.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_kbnode_hash (cdk_kbnode_t node, gcry_md_hd_t md, int is_v4,
|
||||
int pkttype, int flags)
|
||||
{
|
||||
cdk_packet_t pkt;
|
||||
|
||||
if (!node || !md)
|
||||
return CDK_Inv_Value;
|
||||
if (!pkttype)
|
||||
{
|
||||
pkt = cdk_kbnode_get_packet (node);
|
||||
pkttype = pkt->pkttype;
|
||||
}
|
||||
else
|
||||
{
|
||||
pkt = cdk_kbnode_find_packet (node, pkttype);
|
||||
if (!pkt)
|
||||
return CDK_Inv_Packet;
|
||||
}
|
||||
|
||||
switch (pkttype)
|
||||
{
|
||||
case CDK_PKT_PUBLIC_KEY:
|
||||
case CDK_PKT_PUBLIC_SUBKEY:
|
||||
_cdk_hash_pubkey (pkt->pkt.public_key, md, flags & 1);
|
||||
break;
|
||||
|
||||
case CDK_PKT_USER_ID:
|
||||
_cdk_hash_userid (pkt->pkt.user_id, is_v4, md);
|
||||
break;
|
||||
|
||||
case CDK_PKT_SIGNATURE:
|
||||
_cdk_hash_sig_data (pkt->pkt.signature, md);
|
||||
break;
|
||||
|
||||
default:
|
||||
return CDK_Inv_Mode;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,2303 +0,0 @@
|
||||
/* keydb.c - Key database routines
|
||||
* Copyright (C) 2002, 2003, 2007 Timo Schulz
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "opencdk.h"
|
||||
#include "main.h"
|
||||
#include "packet.h"
|
||||
#include "filters.h"
|
||||
#include "stream.h"
|
||||
|
||||
|
||||
#define KEYID_CMP(a, b) ((a[0]) == (b[0]) && (a[1]) == (b[1]))
|
||||
#define KEYDB_CACHE_ENTRIES 8
|
||||
|
||||
|
||||
/* Internal key index structure. */
|
||||
struct key_idx_s
|
||||
{
|
||||
off_t offset;
|
||||
u32 keyid[2];
|
||||
byte fpr[KEY_FPR_LEN];
|
||||
};
|
||||
typedef struct key_idx_s *key_idx_t;
|
||||
|
||||
|
||||
/* Internal handle for the search operation. */
|
||||
struct cdk_dbsearch_s
|
||||
{
|
||||
union
|
||||
{
|
||||
char *pattern; /* A search is performed by pattern. */
|
||||
u32 keyid[2]; /* A search by keyid. */
|
||||
byte fpr[KEY_FPR_LEN]; /* A search by fingerprint. */
|
||||
} u;
|
||||
int type;
|
||||
};
|
||||
typedef struct cdk_dbsearch_s *cdk_dbsearch_t;
|
||||
|
||||
/* Internal key cache to associate a key with an file offset. */
|
||||
struct key_table_s
|
||||
{
|
||||
struct key_table_s *next;
|
||||
off_t offset;
|
||||
cdk_dbsearch_t desc;
|
||||
};
|
||||
typedef struct key_table_s *key_table_t;
|
||||
|
||||
/* Internal key database handle. */
|
||||
struct cdk_keydb_hd_s
|
||||
{
|
||||
int type; /* type of the key db handle. */
|
||||
int fp_ref; /* 1=means it is a reference and shall not be closed. */
|
||||
cdk_stream_t fp;
|
||||
cdk_stream_t idx;
|
||||
cdk_dbsearch_t dbs;
|
||||
char *name; /* name of the underlying file or NULL. */
|
||||
char *idx_name; /* name of the index file or NULL. */
|
||||
struct key_table_s *cache;
|
||||
size_t ncache;
|
||||
unsigned int secret:1; /* contain secret keys. */
|
||||
unsigned int isopen:1; /* the underlying stream is opened. */
|
||||
unsigned int no_cache:1; /* disable the index cache. */
|
||||
unsigned int search:1; /* handle is in search mode. */
|
||||
|
||||
/* structure to store some stats about the keydb. */
|
||||
struct
|
||||
{
|
||||
size_t new_keys; /* amount of new keys that were imported. */
|
||||
} stats;
|
||||
};
|
||||
|
||||
|
||||
static void keydb_cache_free (key_table_t cache);
|
||||
static int keydb_search_copy (cdk_dbsearch_t * r_dst, cdk_dbsearch_t src);
|
||||
static void keydb_search_free (cdk_dbsearch_t dbs);
|
||||
static int classify_data (const byte * buf, size_t len);
|
||||
static cdk_kbnode_t find_selfsig_node (cdk_kbnode_t key, cdk_pkt_pubkey_t pk);
|
||||
|
||||
|
||||
static char *
|
||||
keydb_idx_mkname (const char *file)
|
||||
{
|
||||
char *fname, *fmt;
|
||||
|
||||
fmt = "%s.idx";
|
||||
fname = cdk_calloc (1, strlen (file) + strlen (fmt) + 1);
|
||||
if (!fname)
|
||||
return NULL;
|
||||
sprintf (fname, fmt, file);
|
||||
return fname;
|
||||
}
|
||||
|
||||
|
||||
/* This functions builds an index of the keyring into a separate file
|
||||
with the name keyring.ext.idx. It contains the offset of all public-
|
||||
and public subkeys. The format of the file is:
|
||||
--------
|
||||
4 octets offset of the packet
|
||||
8 octets keyid
|
||||
20 octets fingerprint
|
||||
--------
|
||||
We store the keyid and the fingerprint due to the fact we can't get
|
||||
the keyid from a v3 fingerprint directly.
|
||||
*/
|
||||
static cdk_error_t
|
||||
keydb_idx_build (const char *file)
|
||||
{
|
||||
cdk_packet_t pkt;
|
||||
cdk_stream_t inp, out = NULL;
|
||||
byte buf[4 + 8 + KEY_FPR_LEN];
|
||||
char *idx_name;
|
||||
u32 keyid[2];
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!file)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
rc = cdk_stream_open (file, &inp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
idx_name = keydb_idx_mkname (file);
|
||||
if (!idx_name)
|
||||
{
|
||||
cdk_stream_close (inp);
|
||||
return CDK_Out_Of_Core;
|
||||
}
|
||||
rc = cdk_stream_create (idx_name, &out);
|
||||
cdk_free (idx_name);
|
||||
if (rc)
|
||||
{
|
||||
cdk_stream_close (inp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
cdk_pkt_new (&pkt);
|
||||
while (!cdk_stream_eof (inp))
|
||||
{
|
||||
off_t pos = cdk_stream_tell (inp);
|
||||
|
||||
rc = cdk_pkt_read (inp, pkt);
|
||||
if (rc)
|
||||
{
|
||||
_cdk_log_debug ("index build failed packet off=%lu\n", pos);
|
||||
/* FIXME: The index is incomplete */
|
||||
break;
|
||||
}
|
||||
if (pkt->pkttype == CDK_PKT_PUBLIC_KEY ||
|
||||
pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY)
|
||||
{
|
||||
_cdk_u32tobuf (pos, buf);
|
||||
cdk_pk_get_keyid (pkt->pkt.public_key, keyid);
|
||||
_cdk_u32tobuf (keyid[0], buf + 4);
|
||||
_cdk_u32tobuf (keyid[1], buf + 8);
|
||||
cdk_pk_get_fingerprint (pkt->pkt.public_key, buf + 12);
|
||||
cdk_stream_write (out, buf, 4 + 8 + KEY_FPR_LEN);
|
||||
}
|
||||
cdk_pkt_free (pkt);
|
||||
}
|
||||
|
||||
cdk_pkt_release (pkt);
|
||||
|
||||
cdk_stream_close (out);
|
||||
cdk_stream_close (inp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_keydb_idx_rebuild:
|
||||
* @hd: key database handle
|
||||
*
|
||||
* Rebuild the key index files for the given key database.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_keydb_idx_rebuild (cdk_keydb_hd_t db)
|
||||
{
|
||||
struct stat stbuf;
|
||||
char *tmp_idx_name;
|
||||
cdk_error_t rc;
|
||||
int err;
|
||||
|
||||
if (!db || !db->name)
|
||||
return CDK_Inv_Value;
|
||||
if (db->secret)
|
||||
return 0;
|
||||
|
||||
tmp_idx_name = keydb_idx_mkname (db->name);
|
||||
if (!tmp_idx_name)
|
||||
return CDK_Out_Of_Core;
|
||||
err = stat (tmp_idx_name, &stbuf);
|
||||
cdk_free (tmp_idx_name);
|
||||
/* This function expects an existing index which can be rebuild,
|
||||
if no index exists we do not build one and just return. */
|
||||
if (err)
|
||||
return 0;
|
||||
|
||||
cdk_stream_close (db->idx);
|
||||
db->idx = NULL;
|
||||
if (!db->idx_name)
|
||||
{
|
||||
db->idx_name = keydb_idx_mkname (db->name);
|
||||
if (!db->idx_name)
|
||||
return CDK_Out_Of_Core;
|
||||
}
|
||||
rc = keydb_idx_build (db->name);
|
||||
if (!rc)
|
||||
rc = cdk_stream_open (db->idx_name, &db->idx);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
keydb_idx_parse (cdk_stream_t inp, key_idx_t * r_idx)
|
||||
{
|
||||
key_idx_t idx;
|
||||
byte buf[4];
|
||||
|
||||
if (!inp || !r_idx)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
idx = cdk_calloc (1, sizeof *idx);
|
||||
if (!idx)
|
||||
return CDK_Out_Of_Core;
|
||||
|
||||
while (!cdk_stream_eof (inp))
|
||||
{
|
||||
if (cdk_stream_read (inp, buf, 4) == CDK_EOF)
|
||||
break;
|
||||
idx->offset = _cdk_buftou32 (buf);
|
||||
cdk_stream_read (inp, buf, 4);
|
||||
idx->keyid[0] = _cdk_buftou32 (buf);
|
||||
cdk_stream_read (inp, buf, 4);
|
||||
idx->keyid[1] = _cdk_buftou32 (buf);
|
||||
cdk_stream_read (inp, idx->fpr, KEY_FPR_LEN);
|
||||
break;
|
||||
}
|
||||
*r_idx = idx;
|
||||
return cdk_stream_eof (inp) ? CDK_EOF : 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
keydb_idx_search (cdk_stream_t inp, u32 * keyid, const byte * fpr,
|
||||
off_t * r_off)
|
||||
{
|
||||
key_idx_t idx;
|
||||
|
||||
if (!inp || !r_off)
|
||||
return CDK_Inv_Value;
|
||||
if ((keyid && fpr) || (!keyid && !fpr))
|
||||
return CDK_Inv_Mode;
|
||||
|
||||
/* We need an initialize the offset var with a value
|
||||
because it might be possible the returned offset will
|
||||
be 0 and then we cannot differ between the begin and an EOF. */
|
||||
*r_off = 0xFFFFFFFF;
|
||||
cdk_stream_seek (inp, 0);
|
||||
while (keydb_idx_parse (inp, &idx) != CDK_EOF)
|
||||
{
|
||||
if (keyid && KEYID_CMP (keyid, idx->keyid))
|
||||
{
|
||||
*r_off = idx->offset;
|
||||
break;
|
||||
}
|
||||
else if (fpr && !memcmp (idx->fpr, fpr, KEY_FPR_LEN))
|
||||
{
|
||||
*r_off = idx->offset;
|
||||
break;
|
||||
}
|
||||
cdk_free (idx);
|
||||
idx = NULL;
|
||||
}
|
||||
cdk_free (idx);
|
||||
return *r_off != 0xFFFFFFFF ? 0 : CDK_EOF;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_keydb_new_from_mem:
|
||||
* @r_hd: The keydb output handle.
|
||||
* @data: The raw key data.
|
||||
* @datlen: The length of the raw data.
|
||||
*
|
||||
* Create a new keyring db handle from the contents of a buffer.
|
||||
*/
|
||||
cdk_error_t
|
||||
cdk_keydb_new_from_mem (cdk_keydb_hd_t * r_db, int secret,
|
||||
const void *data, size_t datlen)
|
||||
{
|
||||
cdk_keydb_hd_t db;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!r_db)
|
||||
return CDK_Inv_Value;
|
||||
*r_db = NULL;
|
||||
db = calloc (1, sizeof *db);
|
||||
rc = cdk_stream_tmp_from_mem (data, datlen, &db->fp);
|
||||
if (!db->fp)
|
||||
{
|
||||
cdk_free (db);
|
||||
return rc;
|
||||
}
|
||||
if (cdk_armor_filter_use (db->fp))
|
||||
cdk_stream_set_armor_flag (db->fp, 0);
|
||||
db->type = CDK_DBTYPE_DATA;
|
||||
db->secret = secret;
|
||||
*r_db = db;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_keydb_new_from_stream:
|
||||
* @r_hd: the output keydb handle
|
||||
* @secret: does the stream contain secret key data
|
||||
* @in: the input stream to use
|
||||
*
|
||||
* This function creates a new keydb handle based on the given
|
||||
* stream. The stream is not closed in cdk_keydb_free() and it
|
||||
* is up to the caller to close it. No decoding is done.
|
||||
*/
|
||||
cdk_error_t
|
||||
cdk_keydb_new_from_stream (cdk_keydb_hd_t * r_hd, int secret, cdk_stream_t in)
|
||||
{
|
||||
cdk_keydb_hd_t hd;
|
||||
|
||||
if (!r_hd)
|
||||
return CDK_Inv_Value;
|
||||
*r_hd = NULL;
|
||||
|
||||
hd = calloc (1, sizeof *hd);
|
||||
hd->fp = in;
|
||||
hd->fp_ref = 1;
|
||||
hd->type = CDK_DBTYPE_STREAM;
|
||||
hd->secret = secret;
|
||||
*r_hd = hd;
|
||||
|
||||
/* We do not push any filters and thus we expect that the format
|
||||
of the stream has the format the user wanted. */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
cdk_keydb_new_from_file (cdk_keydb_hd_t * r_hd, int secret, const char *fname)
|
||||
{
|
||||
cdk_keydb_hd_t hd;
|
||||
|
||||
if (!r_hd)
|
||||
return CDK_Inv_Value;
|
||||
*r_hd = NULL;
|
||||
hd = calloc (1, sizeof *hd);
|
||||
hd->name = cdk_strdup (fname);
|
||||
if (!hd->name)
|
||||
{
|
||||
cdk_free (hd);
|
||||
return CDK_Out_Of_Core;
|
||||
}
|
||||
hd->type = secret ? CDK_DBTYPE_SK_KEYRING : CDK_DBTYPE_PK_KEYRING;
|
||||
hd->secret = secret;
|
||||
*r_hd = hd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* cdk_keydb_new:
|
||||
* @r_hd: handle to store the new keydb object
|
||||
* @type: type of the keyring
|
||||
* @data: data which depends on the keyring type
|
||||
* @count: length of the data
|
||||
*
|
||||
* Create a new keydb object
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_keydb_new (cdk_keydb_hd_t * r_hd, int type, void *data, size_t count)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CDK_DBTYPE_PK_KEYRING:
|
||||
case CDK_DBTYPE_SK_KEYRING:
|
||||
return cdk_keydb_new_from_file (r_hd, type == CDK_DBTYPE_SK_KEYRING,
|
||||
(const char *) data);
|
||||
|
||||
case CDK_DBTYPE_DATA:
|
||||
return cdk_keydb_new_from_mem (r_hd, 0, data, count);
|
||||
|
||||
case CDK_DBTYPE_STREAM:
|
||||
return cdk_keydb_new_from_stream (r_hd, 0, (cdk_stream_t) data);
|
||||
|
||||
default:
|
||||
return CDK_Inv_Mode;
|
||||
}
|
||||
return CDK_Inv_Mode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_keydb_free:
|
||||
* @hd: the keydb object
|
||||
*
|
||||
* Free the keydb object.
|
||||
**/
|
||||
void
|
||||
cdk_keydb_free (cdk_keydb_hd_t hd)
|
||||
{
|
||||
if (!hd)
|
||||
return;
|
||||
|
||||
if (hd->name)
|
||||
{
|
||||
cdk_free (hd->name);
|
||||
hd->name = NULL;
|
||||
}
|
||||
|
||||
if (hd->fp && !hd->fp_ref)
|
||||
{
|
||||
cdk_stream_close (hd->fp);
|
||||
hd->fp = NULL;
|
||||
}
|
||||
|
||||
if (hd->idx)
|
||||
{
|
||||
cdk_stream_close (hd->idx);
|
||||
hd->idx = NULL;
|
||||
}
|
||||
|
||||
hd->isopen = 0;
|
||||
hd->no_cache = 0;
|
||||
hd->secret = 0;
|
||||
keydb_cache_free (hd->cache);
|
||||
hd->cache = NULL;
|
||||
keydb_search_free (hd->dbs);
|
||||
hd->dbs = NULL;
|
||||
cdk_free (hd);
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
_cdk_keydb_open (cdk_keydb_hd_t hd, cdk_stream_t * ret_kr)
|
||||
{
|
||||
cdk_error_t rc, ec;
|
||||
|
||||
if (!hd || !ret_kr)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
rc = 0;
|
||||
if ((hd->type == CDK_DBTYPE_DATA || hd->type == CDK_DBTYPE_STREAM)
|
||||
&& hd->fp)
|
||||
cdk_stream_seek (hd->fp, 0);
|
||||
else if (hd->type == CDK_DBTYPE_PK_KEYRING ||
|
||||
hd->type == CDK_DBTYPE_SK_KEYRING)
|
||||
{
|
||||
if (!hd->isopen && hd->name)
|
||||
{
|
||||
rc = cdk_stream_open (hd->name, &hd->fp);
|
||||
if (rc)
|
||||
goto leave;
|
||||
if (cdk_armor_filter_use (hd->fp))
|
||||
cdk_stream_set_armor_flag (hd->fp, 0);
|
||||
hd->isopen = 1;
|
||||
/* We disable the index cache for smaller keyrings. */
|
||||
if (cdk_stream_get_length (hd->fp) < 524288)
|
||||
{
|
||||
hd->no_cache = 1;
|
||||
goto leave;
|
||||
}
|
||||
cdk_free (hd->idx_name);
|
||||
hd->idx_name = keydb_idx_mkname (hd->name);
|
||||
if (!hd->idx_name)
|
||||
{
|
||||
rc = CDK_Out_Of_Core;
|
||||
goto leave;
|
||||
}
|
||||
ec = cdk_stream_open (hd->idx_name, &hd->idx);
|
||||
if (ec && !hd->secret)
|
||||
{
|
||||
rc = keydb_idx_build (hd->name);
|
||||
if (!rc)
|
||||
rc = cdk_stream_open (hd->idx_name, &hd->idx);
|
||||
if (!rc)
|
||||
_cdk_log_debug ("create key index table\n");
|
||||
else
|
||||
{
|
||||
/* This is no real error, it just means we can't create
|
||||
the index at the given directory. maybe we've no write
|
||||
access. in this case, we simply disable the index. */
|
||||
_cdk_log_debug ("disable key index table err=%d\n", rc);
|
||||
rc = 0;
|
||||
hd->no_cache = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We use the cache to search keys, so we always rewind the
|
||||
STREAM. Except when the _NEXT search mode is used because
|
||||
this mode is an enumeration and no seeking is needed. */
|
||||
if (!hd->search ||
|
||||
(hd->search && hd->dbs->type != CDK_DBSEARCH_NEXT))
|
||||
cdk_stream_seek (hd->fp, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
return CDK_Inv_Mode;
|
||||
|
||||
leave:
|
||||
if (rc)
|
||||
{
|
||||
cdk_stream_close (hd->fp);
|
||||
hd->fp = NULL;
|
||||
}
|
||||
*ret_kr = hd->fp;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
find_by_keyid (cdk_kbnode_t knode, cdk_dbsearch_t ks)
|
||||
{
|
||||
cdk_kbnode_t node;
|
||||
u32 keyid[2];
|
||||
|
||||
for (node = knode; node; node = node->next)
|
||||
{
|
||||
if (node->pkt->pkttype == CDK_PKT_PUBLIC_KEY ||
|
||||
node->pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY ||
|
||||
node->pkt->pkttype == CDK_PKT_SECRET_KEY ||
|
||||
node->pkt->pkttype == CDK_PKT_SECRET_SUBKEY)
|
||||
{
|
||||
_cdk_pkt_get_keyid (node->pkt, keyid);
|
||||
switch (ks->type)
|
||||
{
|
||||
case CDK_DBSEARCH_SHORT_KEYID:
|
||||
if (keyid[1] == ks->u.keyid[1])
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case CDK_DBSEARCH_KEYID:
|
||||
if (KEYID_CMP (keyid, ks->u.keyid))
|
||||
return 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
_cdk_log_debug ("find_by_keyid: invalid mode = %d\n", ks->type);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
find_by_fpr (cdk_kbnode_t knode, cdk_dbsearch_t ks)
|
||||
{
|
||||
cdk_kbnode_t node;
|
||||
byte fpr[KEY_FPR_LEN];
|
||||
|
||||
if (ks->type != CDK_DBSEARCH_FPR)
|
||||
return 0;
|
||||
|
||||
for (node = knode; node; node = node->next)
|
||||
{
|
||||
if (node->pkt->pkttype == CDK_PKT_PUBLIC_KEY ||
|
||||
node->pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY ||
|
||||
node->pkt->pkttype == CDK_PKT_SECRET_KEY ||
|
||||
node->pkt->pkttype == CDK_PKT_SECRET_SUBKEY)
|
||||
{
|
||||
_cdk_pkt_get_fingerprint (node->pkt, fpr);
|
||||
if (!memcmp (ks->u.fpr, fpr, KEY_FPR_LEN))
|
||||
return 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
find_by_pattern (cdk_kbnode_t knode, cdk_dbsearch_t ks)
|
||||
{
|
||||
cdk_kbnode_t node;
|
||||
size_t uidlen;
|
||||
char *name;
|
||||
|
||||
for (node = knode; node; node = node->next)
|
||||
{
|
||||
if (node->pkt->pkttype != CDK_PKT_USER_ID)
|
||||
continue;
|
||||
if (node->pkt->pkt.user_id->attrib_img != NULL)
|
||||
continue; /* Skip attribute packets. */
|
||||
uidlen = node->pkt->pkt.user_id->len;
|
||||
name = node->pkt->pkt.user_id->name;
|
||||
switch (ks->type)
|
||||
{
|
||||
case CDK_DBSEARCH_EXACT:
|
||||
if (name &&
|
||||
(strlen (ks->u.pattern) == uidlen &&
|
||||
!strncmp (ks->u.pattern, name, uidlen)))
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case CDK_DBSEARCH_SUBSTR:
|
||||
if (uidlen > 65536)
|
||||
break;
|
||||
if (name && strlen (ks->u.pattern) > uidlen)
|
||||
break;
|
||||
if (name && _cdk_memistr (name, uidlen, ks->u.pattern))
|
||||
return 1;
|
||||
break;
|
||||
|
||||
default: /* Invalid mode */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
keydb_search_free (cdk_dbsearch_t dbs)
|
||||
{
|
||||
if (!dbs)
|
||||
return;
|
||||
if (dbs->type == CDK_DBSEARCH_EXACT || dbs->type == CDK_DBSEARCH_SUBSTR)
|
||||
cdk_free (dbs->u.pattern);
|
||||
dbs->type = 0;
|
||||
cdk_free (dbs);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
keydb_cache_free (key_table_t cache)
|
||||
{
|
||||
key_table_t c2;
|
||||
|
||||
while (cache)
|
||||
{
|
||||
c2 = cache->next;
|
||||
cache->offset = 0;
|
||||
keydb_search_free (cache->desc);
|
||||
cdk_free (cache);
|
||||
cache = c2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static key_table_t
|
||||
keydb_cache_find (key_table_t cache, cdk_dbsearch_t desc)
|
||||
{
|
||||
key_table_t t;
|
||||
|
||||
for (t = cache; t; t = t->next)
|
||||
{
|
||||
if (t->desc->type != desc->type)
|
||||
continue;
|
||||
switch (t->desc->type)
|
||||
{
|
||||
case CDK_DBSEARCH_SHORT_KEYID:
|
||||
case CDK_DBSEARCH_KEYID:
|
||||
if (KEYID_CMP (t->desc->u.keyid, desc->u.keyid))
|
||||
return t;
|
||||
break;
|
||||
|
||||
case CDK_DBSEARCH_EXACT:
|
||||
if (strlen (t->desc->u.pattern) == strlen (desc->u.pattern) &&
|
||||
!strcmp (t->desc->u.pattern, desc->u.pattern))
|
||||
return t;
|
||||
break;
|
||||
|
||||
case CDK_DBSEARCH_SUBSTR:
|
||||
if (strstr (t->desc->u.pattern, desc->u.pattern))
|
||||
return t;
|
||||
break;
|
||||
|
||||
case CDK_DBSEARCH_FPR:
|
||||
if (!memcmp (t->desc->u.fpr, desc->u.fpr, KEY_FPR_LEN))
|
||||
return t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
keydb_cache_add (cdk_keydb_hd_t hd, cdk_dbsearch_t dbs, off_t offset)
|
||||
{
|
||||
key_table_t k;
|
||||
|
||||
if (!hd)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (hd->ncache > KEYDB_CACHE_ENTRIES)
|
||||
return 0; /* FIXME: we should replace the last entry. */
|
||||
k = cdk_calloc (1, sizeof *k);
|
||||
if (!k)
|
||||
return CDK_Out_Of_Core;
|
||||
k->offset = offset;
|
||||
keydb_search_copy (&k->desc, dbs);
|
||||
k->next = hd->cache;
|
||||
hd->cache = k;
|
||||
hd->ncache++;
|
||||
_cdk_log_debug ("cache: add entry off=%d type=%d\n", offset, dbs->type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
keydb_search_copy (cdk_dbsearch_t * r_dst, cdk_dbsearch_t src)
|
||||
{
|
||||
cdk_dbsearch_t dst;
|
||||
|
||||
if (!r_dst || !src)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
*r_dst = NULL;
|
||||
dst = cdk_calloc (1, sizeof *dst);
|
||||
if (!dst)
|
||||
return CDK_Out_Of_Core;
|
||||
dst->type = src->type;
|
||||
switch (src->type)
|
||||
{
|
||||
case CDK_DBSEARCH_EXACT:
|
||||
case CDK_DBSEARCH_SUBSTR:
|
||||
dst->u.pattern = cdk_strdup (src->u.pattern);
|
||||
if (!dst->u.pattern)
|
||||
return CDK_Out_Of_Core;
|
||||
break;
|
||||
|
||||
case CDK_DBSEARCH_SHORT_KEYID:
|
||||
case CDK_DBSEARCH_KEYID:
|
||||
dst->u.keyid[0] = src->u.keyid[0];
|
||||
dst->u.keyid[1] = src->u.keyid[1];
|
||||
break;
|
||||
|
||||
case CDK_DBSEARCH_FPR:
|
||||
memcpy (dst->u.fpr, src->u.fpr, KEY_FPR_LEN);
|
||||
break;
|
||||
}
|
||||
*r_dst = dst;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_keydb_search_start:
|
||||
* @db: key database handle
|
||||
* @type: specifies the search type
|
||||
* @desc: description which depends on the type
|
||||
*
|
||||
* Create a new keydb search object.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_keydb_search_start (cdk_keydb_hd_t db, int type, void *desc)
|
||||
{
|
||||
cdk_dbsearch_t dbs;
|
||||
u32 *keyid;
|
||||
char *p, tmp[3];
|
||||
int i;
|
||||
|
||||
if (!db)
|
||||
return CDK_Inv_Value;
|
||||
if (type != CDK_DBSEARCH_NEXT && !desc)
|
||||
return CDK_Inv_Mode;
|
||||
|
||||
dbs = cdk_calloc (1, sizeof *dbs);
|
||||
if (!dbs)
|
||||
return CDK_Out_Of_Core;
|
||||
dbs->type = type;
|
||||
switch (type)
|
||||
{
|
||||
case CDK_DBSEARCH_EXACT:
|
||||
case CDK_DBSEARCH_SUBSTR:
|
||||
cdk_free (dbs->u.pattern);
|
||||
dbs->u.pattern = cdk_strdup (desc);
|
||||
if (!dbs->u.pattern)
|
||||
{
|
||||
cdk_free (dbs);
|
||||
return CDK_Out_Of_Core;
|
||||
}
|
||||
break;
|
||||
|
||||
case CDK_DBSEARCH_SHORT_KEYID:
|
||||
keyid = desc;
|
||||
dbs->u.keyid[1] = keyid[0];
|
||||
break;
|
||||
|
||||
case CDK_DBSEARCH_KEYID:
|
||||
keyid = desc;
|
||||
dbs->u.keyid[0] = keyid[0];
|
||||
dbs->u.keyid[1] = keyid[1];
|
||||
break;
|
||||
|
||||
case CDK_DBSEARCH_FPR:
|
||||
memcpy (dbs->u.fpr, desc, KEY_FPR_LEN);
|
||||
break;
|
||||
|
||||
case CDK_DBSEARCH_NEXT:
|
||||
break;
|
||||
|
||||
case CDK_DBSEARCH_AUTO:
|
||||
/* Override the type with the actual db search type. */
|
||||
dbs->type = classify_data (desc, strlen (desc));
|
||||
switch (dbs->type)
|
||||
{
|
||||
case CDK_DBSEARCH_SUBSTR:
|
||||
case CDK_DBSEARCH_EXACT:
|
||||
cdk_free (dbs->u.pattern);
|
||||
p = dbs->u.pattern = cdk_strdup (desc);
|
||||
if (!p)
|
||||
{
|
||||
cdk_free (dbs);
|
||||
return CDK_Out_Of_Core;
|
||||
}
|
||||
break;
|
||||
|
||||
case CDK_DBSEARCH_SHORT_KEYID:
|
||||
case CDK_DBSEARCH_KEYID:
|
||||
p = desc;
|
||||
if (!strncmp (p, "0x", 2))
|
||||
p += 2;
|
||||
if (strlen (p) == 8)
|
||||
{
|
||||
dbs->u.keyid[0] = 0;
|
||||
dbs->u.keyid[1] = strtoul (p, NULL, 16);
|
||||
}
|
||||
else if (strlen (p) == 16)
|
||||
{
|
||||
dbs->u.keyid[0] = strtoul (p, NULL, 16);
|
||||
dbs->u.keyid[1] = strtoul (p + 8, NULL, 16);
|
||||
}
|
||||
else
|
||||
{ /* Invalid key ID object. */
|
||||
cdk_free (dbs);
|
||||
return CDK_Inv_Mode;
|
||||
}
|
||||
break;
|
||||
|
||||
case CDK_DBSEARCH_FPR:
|
||||
p = desc;
|
||||
if (strlen (p) != 2 * KEY_FPR_LEN)
|
||||
{
|
||||
cdk_free (dbs);
|
||||
return CDK_Inv_Mode;
|
||||
}
|
||||
for (i = 0; i < KEY_FPR_LEN; i++)
|
||||
{
|
||||
tmp[0] = p[2 * i];
|
||||
tmp[1] = p[2 * i + 1];
|
||||
tmp[2] = 0x00;
|
||||
dbs->u.fpr[i] = strtoul (tmp, NULL, 16);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
cdk_free (dbs);
|
||||
_cdk_log_debug ("cdk_keydb_search_start: invalid mode = %d\n", type);
|
||||
return CDK_Inv_Mode;
|
||||
}
|
||||
|
||||
keydb_search_free (db->dbs);
|
||||
db->dbs = dbs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
keydb_pos_from_cache (cdk_keydb_hd_t hd, cdk_dbsearch_t ks,
|
||||
int *r_cache_hit, off_t * r_off)
|
||||
{
|
||||
key_table_t c;
|
||||
|
||||
if (!hd || !r_cache_hit || !r_off)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
/* Reset the values. */
|
||||
*r_cache_hit = 0;
|
||||
*r_off = 0;
|
||||
|
||||
c = keydb_cache_find (hd->cache, ks);
|
||||
if (c != NULL)
|
||||
{
|
||||
_cdk_log_debug ("cache: found entry in cache.\n");
|
||||
*r_cache_hit = 1;
|
||||
*r_off = c->offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* No index cache available so we just return here. */
|
||||
if (!hd->idx)
|
||||
return 0;
|
||||
|
||||
if (hd->idx)
|
||||
{
|
||||
if (ks->type == CDK_DBSEARCH_KEYID)
|
||||
{
|
||||
if (keydb_idx_search (hd->idx, ks->u.keyid, NULL, r_off))
|
||||
return CDK_Error_No_Key;
|
||||
_cdk_log_debug ("cache: found keyid entry in idx table.\n");
|
||||
*r_cache_hit = 1;
|
||||
}
|
||||
else if (ks->type == CDK_DBSEARCH_FPR)
|
||||
{
|
||||
if (keydb_idx_search (hd->idx, NULL, ks->u.fpr, r_off))
|
||||
return CDK_Error_No_Key;
|
||||
_cdk_log_debug ("cache: found fpr entry in idx table.\n");
|
||||
*r_cache_hit = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_keydb_search:
|
||||
* @hd: the keydb object
|
||||
* @ks: the keydb search object
|
||||
* @ret_key: kbnode object to store the key
|
||||
*
|
||||
* Search for a key in the given keyring. The search mode is handled
|
||||
* via @ks. If the key was found, @ret_key contains the key data.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_keydb_search (cdk_keydb_hd_t hd, cdk_kbnode_t * ret_key)
|
||||
{
|
||||
cdk_stream_t kr;
|
||||
cdk_kbnode_t knode;
|
||||
cdk_dbsearch_t ks;
|
||||
cdk_error_t rc = 0;
|
||||
off_t pos = 0, off = 0;
|
||||
int key_found = 0, cache_hit = 0;
|
||||
|
||||
if (!hd || !ret_key)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
*ret_key = NULL;
|
||||
kr = NULL;
|
||||
hd->search = 1;
|
||||
rc = _cdk_keydb_open (hd, &kr);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!hd->no_cache)
|
||||
{
|
||||
/* It is possible the index is not up-to-date and thus we do
|
||||
not find the requesed key. In this case, we reset cache hit
|
||||
and continue our normal search procedure. */
|
||||
rc = keydb_pos_from_cache (hd, hd->dbs, &cache_hit, &off);
|
||||
if (rc)
|
||||
cache_hit = 0;
|
||||
}
|
||||
|
||||
knode = NULL;
|
||||
ks = hd->dbs;
|
||||
while (!key_found && !rc)
|
||||
{
|
||||
if (cache_hit && ks->type != CDK_DBSEARCH_NEXT)
|
||||
cdk_stream_seek (kr, off);
|
||||
pos = cdk_stream_tell (kr);
|
||||
rc = cdk_keydb_get_keyblock (kr, &knode);
|
||||
if (rc)
|
||||
{
|
||||
if (rc == CDK_EOF)
|
||||
break;
|
||||
else
|
||||
return rc;
|
||||
}
|
||||
|
||||
switch (ks->type)
|
||||
{
|
||||
case CDK_DBSEARCH_SHORT_KEYID:
|
||||
case CDK_DBSEARCH_KEYID:
|
||||
key_found = find_by_keyid (knode, ks);
|
||||
break;
|
||||
|
||||
case CDK_DBSEARCH_FPR:
|
||||
key_found = find_by_fpr (knode, ks);
|
||||
break;
|
||||
|
||||
case CDK_DBSEARCH_EXACT:
|
||||
case CDK_DBSEARCH_SUBSTR:
|
||||
key_found = find_by_pattern (knode, ks);
|
||||
break;
|
||||
|
||||
case CDK_DBSEARCH_NEXT:
|
||||
key_found = knode ? 1 : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (key_found)
|
||||
{
|
||||
if (!keydb_cache_find (hd->cache, ks))
|
||||
keydb_cache_add (hd, ks, pos);
|
||||
break;
|
||||
}
|
||||
|
||||
cdk_kbnode_release (knode);
|
||||
knode = NULL;
|
||||
}
|
||||
|
||||
hd->search = 0;
|
||||
if (key_found && rc == CDK_EOF)
|
||||
rc = 0;
|
||||
else if (rc == CDK_EOF && !key_found)
|
||||
rc = CDK_Error_No_Key;
|
||||
*ret_key = key_found ? knode : NULL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
cdk_keydb_get_bykeyid (cdk_keydb_hd_t hd, u32 * keyid, cdk_kbnode_t * ret_key)
|
||||
{
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!hd || !keyid || !ret_key)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
rc = cdk_keydb_search_start (hd, CDK_DBSEARCH_KEYID, keyid);
|
||||
if (!rc)
|
||||
rc = cdk_keydb_search (hd, ret_key);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
cdk_keydb_get_byfpr (cdk_keydb_hd_t hd, const byte * fpr,
|
||||
cdk_kbnode_t * r_key)
|
||||
{
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!hd || !fpr || !r_key)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
rc = cdk_keydb_search_start (hd, CDK_DBSEARCH_FPR, (byte *) fpr);
|
||||
if (!rc)
|
||||
rc = cdk_keydb_search (hd, r_key);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
cdk_keydb_get_bypattern (cdk_keydb_hd_t hd, const char *patt,
|
||||
cdk_kbnode_t * ret_key)
|
||||
{
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!hd || !patt || !ret_key)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
rc = cdk_keydb_search_start (hd, CDK_DBSEARCH_SUBSTR, (char *) patt);
|
||||
if (!rc)
|
||||
rc = cdk_keydb_search (hd, ret_key);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
keydb_check_key (cdk_packet_t pkt)
|
||||
{
|
||||
cdk_pkt_pubkey_t pk;
|
||||
int is_sk, valid;
|
||||
|
||||
if (pkt->pkttype == CDK_PKT_PUBLIC_KEY ||
|
||||
pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY)
|
||||
{
|
||||
pk = pkt->pkt.public_key;
|
||||
is_sk = 0;
|
||||
}
|
||||
else if (pkt->pkttype == CDK_PKT_SECRET_KEY ||
|
||||
pkt->pkttype == CDK_PKT_SECRET_SUBKEY)
|
||||
{
|
||||
pk = pkt->pkt.secret_key->pk;
|
||||
is_sk = 1;
|
||||
}
|
||||
else /* No key object. */
|
||||
return 0;
|
||||
valid = !pk->is_revoked && !pk->has_expired;
|
||||
if (is_sk)
|
||||
return valid;
|
||||
return valid && !pk->is_invalid;
|
||||
}
|
||||
|
||||
|
||||
/* Find the first kbnode with the requested packet type
|
||||
that represents a valid key. */
|
||||
static cdk_kbnode_t
|
||||
kbnode_find_valid (cdk_kbnode_t root, int pkttype)
|
||||
{
|
||||
cdk_kbnode_t n;
|
||||
|
||||
for (n = root; n; n = n->next)
|
||||
{
|
||||
if (n->pkt->pkttype != pkttype)
|
||||
continue;
|
||||
if (keydb_check_key (n->pkt))
|
||||
return n;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static cdk_kbnode_t
|
||||
keydb_find_byusage (cdk_kbnode_t root, int req_usage, int is_pk)
|
||||
{
|
||||
cdk_kbnode_t node, key;
|
||||
int req_type;
|
||||
long timestamp;
|
||||
|
||||
req_type = is_pk ? CDK_PKT_PUBLIC_KEY : CDK_PKT_SECRET_KEY;
|
||||
if (!req_usage)
|
||||
return kbnode_find_valid (root, req_type);
|
||||
|
||||
node = cdk_kbnode_find (root, req_type);
|
||||
if (node && !keydb_check_key (node->pkt))
|
||||
return NULL;
|
||||
|
||||
key = NULL;
|
||||
timestamp = 0;
|
||||
/* We iteratre over the all nodes and search for keys or
|
||||
subkeys which match the usage and which are not invalid.
|
||||
A timestamp is used to figure out the newest valid key. */
|
||||
for (node = root; node; node = node->next)
|
||||
{
|
||||
if (is_pk && (node->pkt->pkttype == CDK_PKT_PUBLIC_KEY ||
|
||||
node->pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY)
|
||||
&& keydb_check_key (node->pkt)
|
||||
&& (node->pkt->pkt.public_key->pubkey_usage & req_usage))
|
||||
{
|
||||
if (node->pkt->pkt.public_key->timestamp > timestamp)
|
||||
key = node;
|
||||
}
|
||||
if (!is_pk && (node->pkt->pkttype == CDK_PKT_SECRET_KEY ||
|
||||
node->pkt->pkttype == CDK_PKT_SECRET_SUBKEY)
|
||||
&& keydb_check_key (node->pkt)
|
||||
&& (node->pkt->pkt.secret_key->pk->pubkey_usage & req_usage))
|
||||
{
|
||||
if (node->pkt->pkt.secret_key->pk->timestamp > timestamp)
|
||||
key = node;
|
||||
}
|
||||
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
static cdk_kbnode_t
|
||||
keydb_find_bykeyid (cdk_kbnode_t root, const u32 * keyid, int search_mode)
|
||||
{
|
||||
cdk_kbnode_t node;
|
||||
u32 kid[2];
|
||||
|
||||
for (node = root; node; node = node->next)
|
||||
{
|
||||
if (!_cdk_pkt_get_keyid (node->pkt, kid))
|
||||
continue;
|
||||
if (search_mode == CDK_DBSEARCH_SHORT_KEYID && kid[1] == keyid[1])
|
||||
return node;
|
||||
else if (kid[0] == keyid[0] && kid[1] == keyid[1])
|
||||
return node;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
_cdk_keydb_get_sk_byusage (cdk_keydb_hd_t hd, const char *name,
|
||||
cdk_seckey_t * ret_sk, int usage)
|
||||
{
|
||||
cdk_kbnode_t knode = NULL;
|
||||
cdk_kbnode_t node, sk_node, pk_node;
|
||||
cdk_pkt_seckey_t sk;
|
||||
cdk_error_t rc;
|
||||
const char *s;
|
||||
int pkttype;
|
||||
|
||||
if (!ret_sk || !usage)
|
||||
return CDK_Inv_Value;
|
||||
if (!hd)
|
||||
return CDK_Error_No_Keyring;
|
||||
|
||||
*ret_sk = NULL;
|
||||
rc = cdk_keydb_search_start (hd, CDK_DBSEARCH_AUTO, (char *) name);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = cdk_keydb_search (hd, &knode);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
sk_node = keydb_find_byusage (knode, usage, 0);
|
||||
if (!sk_node)
|
||||
{
|
||||
cdk_kbnode_release (knode);
|
||||
return CDK_Unusable_Key;
|
||||
}
|
||||
|
||||
/* We clone the node with the secret key to avoid that the
|
||||
packet will be released. */
|
||||
_cdk_kbnode_clone (sk_node);
|
||||
sk = sk_node->pkt->pkt.secret_key;
|
||||
|
||||
for (node = knode; node; node = node->next)
|
||||
{
|
||||
if (node->pkt->pkttype == CDK_PKT_USER_ID)
|
||||
{
|
||||
s = node->pkt->pkt.user_id->name;
|
||||
if (sk && !sk->pk->uid && _cdk_memistr (s, strlen (s), name))
|
||||
{
|
||||
_cdk_copy_userid (&sk->pk->uid, node->pkt->pkt.user_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* To find the self signature, we need the primary public key because
|
||||
the selected secret key might be different from the primary key. */
|
||||
pk_node = cdk_kbnode_find (knode, CDK_PKT_SECRET_KEY);
|
||||
if (!pk_node)
|
||||
{
|
||||
cdk_kbnode_release (knode);
|
||||
return CDK_Unusable_Key;
|
||||
}
|
||||
node = find_selfsig_node (knode, pk_node->pkt->pkt.secret_key->pk);
|
||||
if (sk->pk->uid && node)
|
||||
_cdk_copy_signature (&sk->pk->uid->selfsig, node->pkt->pkt.signature);
|
||||
|
||||
/* We only release the outer packet. */
|
||||
_cdk_pkt_detach_free (sk_node->pkt, &pkttype, (void *) &sk);
|
||||
cdk_kbnode_release (knode);
|
||||
*ret_sk = sk;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
_cdk_keydb_get_pk_byusage (cdk_keydb_hd_t hd, const char *name,
|
||||
cdk_pubkey_t * ret_pk, int usage)
|
||||
{
|
||||
cdk_kbnode_t knode, node, pk_node;
|
||||
cdk_pkt_pubkey_t pk;
|
||||
const char *s;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!ret_pk || !usage)
|
||||
return CDK_Inv_Value;
|
||||
if (!hd)
|
||||
return CDK_Error_No_Keyring;
|
||||
|
||||
*ret_pk = NULL;
|
||||
rc = cdk_keydb_search_start (hd, CDK_DBSEARCH_AUTO, (char *) name);
|
||||
if (!rc)
|
||||
rc = cdk_keydb_search (hd, &knode);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
node = keydb_find_byusage (knode, usage, 1);
|
||||
if (!node)
|
||||
{
|
||||
cdk_kbnode_release (knode);
|
||||
return CDK_Unusable_Key;
|
||||
}
|
||||
|
||||
pk = NULL;
|
||||
_cdk_copy_pubkey (&pk, node->pkt->pkt.public_key);
|
||||
for (node = knode; node; node = node->next)
|
||||
{
|
||||
if (node->pkt->pkttype == CDK_PKT_USER_ID)
|
||||
{
|
||||
s = node->pkt->pkt.user_id->name;
|
||||
if (pk && !pk->uid && _cdk_memistr (s, strlen (s), name))
|
||||
{
|
||||
_cdk_copy_userid (&pk->uid, node->pkt->pkt.user_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Same as in the sk code, the selected key can be a sub key
|
||||
and thus we need the primary key to find the self sig. */
|
||||
pk_node = cdk_kbnode_find (knode, CDK_PKT_PUBLIC_KEY);
|
||||
if (!pk_node)
|
||||
{
|
||||
cdk_kbnode_release (knode);
|
||||
return CDK_Unusable_Key;
|
||||
}
|
||||
node = find_selfsig_node (knode, pk_node->pkt->pkt.public_key);
|
||||
if (pk->uid && node)
|
||||
_cdk_copy_signature (&pk->uid->selfsig, node->pkt->pkt.signature);
|
||||
cdk_kbnode_release (knode);
|
||||
|
||||
*ret_pk = pk;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_keydb_get_pk:
|
||||
* @hd: key db handle
|
||||
* @keyid: keyid of the key
|
||||
* @r_pk: the allocated public key
|
||||
*
|
||||
* Perform a key database search by keyid and return the raw public
|
||||
* key without any signatures or user id's.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_keydb_get_pk (cdk_keydb_hd_t hd, u32 * keyid, cdk_pubkey_t * r_pk)
|
||||
{
|
||||
cdk_kbnode_t knode = NULL, node;
|
||||
cdk_pubkey_t pk;
|
||||
cdk_error_t rc;
|
||||
size_t s_type;
|
||||
int pkttype;
|
||||
|
||||
if (!keyid || !r_pk)
|
||||
return CDK_Inv_Value;
|
||||
if (!hd)
|
||||
return CDK_Error_No_Keyring;
|
||||
|
||||
*r_pk = NULL;
|
||||
s_type = !keyid[0] ? CDK_DBSEARCH_SHORT_KEYID : CDK_DBSEARCH_KEYID;
|
||||
rc = cdk_keydb_search_start (hd, s_type, keyid);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = cdk_keydb_search (hd, &knode);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
node = keydb_find_bykeyid (knode, keyid, s_type);
|
||||
if (!node)
|
||||
{
|
||||
cdk_kbnode_release (knode);
|
||||
return CDK_Error_No_Key;
|
||||
}
|
||||
|
||||
/* See comment in cdk_keydb_get_sk() */
|
||||
_cdk_pkt_detach_free (node->pkt, &pkttype, (void *) &pk);
|
||||
*r_pk = pk;
|
||||
_cdk_kbnode_clone (node);
|
||||
cdk_kbnode_release (knode);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_keydb_get_sk:
|
||||
* @hd: key db handle
|
||||
* @keyid: the keyid of the key
|
||||
* @ret_sk: the allocated secret key
|
||||
*
|
||||
* Perform a key database search by keyid and return
|
||||
* only the raw secret key without the additional nodes,
|
||||
* like the user id or the signatures.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_keydb_get_sk (cdk_keydb_hd_t hd, u32 * keyid, cdk_seckey_t * ret_sk)
|
||||
{
|
||||
cdk_kbnode_t snode, node;
|
||||
cdk_seckey_t sk;
|
||||
cdk_error_t rc;
|
||||
int pkttype;
|
||||
|
||||
if (!keyid || !ret_sk)
|
||||
return CDK_Inv_Value;
|
||||
if (!hd)
|
||||
return CDK_Error_No_Keyring;
|
||||
|
||||
*ret_sk = NULL;
|
||||
rc = cdk_keydb_get_bykeyid (hd, keyid, &snode);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
node = keydb_find_bykeyid (snode, keyid, CDK_DBSEARCH_KEYID);
|
||||
if (!node)
|
||||
{
|
||||
cdk_kbnode_release (snode);
|
||||
return CDK_Error_No_Key;
|
||||
}
|
||||
|
||||
/* We need to release the packet itself but not its contents
|
||||
and thus we detach the openpgp packet and release the structure. */
|
||||
_cdk_pkt_detach_free (node->pkt, &pkttype, (void *) &sk);
|
||||
_cdk_kbnode_clone (node);
|
||||
cdk_kbnode_release (snode);
|
||||
|
||||
*ret_sk = sk;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
is_selfsig (cdk_kbnode_t node, const u32 * keyid)
|
||||
{
|
||||
cdk_pkt_signature_t sig;
|
||||
|
||||
if (node->pkt->pkttype != CDK_PKT_SIGNATURE)
|
||||
return 0;
|
||||
sig = node->pkt->pkt.signature;
|
||||
if ((sig->sig_class >= 0x10 && sig->sig_class <= 0x13) &&
|
||||
sig->keyid[0] == keyid[0] && sig->keyid[1] == keyid[1])
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Find the newest self signature for the public key @pk
|
||||
and return the signature node. */
|
||||
static cdk_kbnode_t
|
||||
find_selfsig_node (cdk_kbnode_t key, cdk_pkt_pubkey_t pk)
|
||||
{
|
||||
cdk_kbnode_t n, sig;
|
||||
unsigned int ts;
|
||||
u32 keyid[2];
|
||||
|
||||
cdk_pk_get_keyid (pk, keyid);
|
||||
sig = NULL;
|
||||
ts = 0;
|
||||
for (n = key; n; n = n->next)
|
||||
{
|
||||
if (is_selfsig (n, keyid) && n->pkt->pkt.signature->timestamp > ts)
|
||||
{
|
||||
ts = n->pkt->pkt.signature->timestamp;
|
||||
sig = n;
|
||||
}
|
||||
}
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
keydb_merge_selfsig (cdk_kbnode_t key, u32 * keyid)
|
||||
{
|
||||
cdk_kbnode_t node, kbnode, unode;
|
||||
cdk_subpkt_t s = NULL;
|
||||
cdk_pkt_signature_t sig = NULL;
|
||||
cdk_pkt_userid_t uid = NULL;
|
||||
const byte *symalg = NULL, *hashalg = NULL, *compalg = NULL;
|
||||
size_t nsymalg = 0, nhashalg = 0, ncompalg = 0, n = 0;
|
||||
size_t key_usage = 0, key_expire = 0;
|
||||
|
||||
if (!key)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
for (node = key; node; node = node->next)
|
||||
{
|
||||
if (!is_selfsig (node, keyid))
|
||||
continue;
|
||||
unode = cdk_kbnode_find_prev (key, node, CDK_PKT_USER_ID);
|
||||
if (!unode)
|
||||
return CDK_Error_No_Key;
|
||||
uid = unode->pkt->pkt.user_id;
|
||||
sig = node->pkt->pkt.signature;
|
||||
s = cdk_subpkt_find (sig->hashed, CDK_SIGSUBPKT_PRIMARY_UID);
|
||||
if (s)
|
||||
uid->is_primary = 1;
|
||||
s = cdk_subpkt_find (sig->hashed, CDK_SIGSUBPKT_FEATURES);
|
||||
if (s && s->size == 1 && s->d[0] & 0x01)
|
||||
uid->mdc_feature = 1;
|
||||
s = cdk_subpkt_find (sig->hashed, CDK_SIGSUBPKT_KEY_EXPIRE);
|
||||
if (s && s->size == 4)
|
||||
key_expire = _cdk_buftou32 (s->d);
|
||||
s = cdk_subpkt_find (sig->hashed, CDK_SIGSUBPKT_KEY_FLAGS);
|
||||
if (s)
|
||||
{
|
||||
if (s->d[0] & 0x03) /* cert + sign data */
|
||||
key_usage |= CDK_KEY_USG_SIGN;
|
||||
if (s->d[0] & 0x0C) /* encrypt comm. + storage */
|
||||
key_usage |= CDK_KEY_USG_ENCR;
|
||||
if (s->d[0] & 0x20)
|
||||
key_usage |= CDK_KEY_USG_AUTH;
|
||||
}
|
||||
s = cdk_subpkt_find (sig->hashed, CDK_SIGSUBPKT_PREFS_SYM);
|
||||
if (s)
|
||||
{
|
||||
symalg = s->d;
|
||||
nsymalg = s->size;
|
||||
n += s->size + 1;
|
||||
}
|
||||
s = cdk_subpkt_find (sig->hashed, CDK_SIGSUBPKT_PREFS_HASH);
|
||||
if (s)
|
||||
{
|
||||
hashalg = s->d;
|
||||
nhashalg = s->size;
|
||||
n += s->size + 1;
|
||||
}
|
||||
s = cdk_subpkt_find (sig->hashed, CDK_SIGSUBPKT_PREFS_ZIP);
|
||||
if (s)
|
||||
{
|
||||
compalg = s->d;
|
||||
ncompalg = s->size;
|
||||
n += s->size + 1;
|
||||
}
|
||||
if (uid->prefs != NULL)
|
||||
cdk_free (uid->prefs);
|
||||
if (!n || !hashalg || !compalg || !symalg)
|
||||
uid->prefs = NULL;
|
||||
else
|
||||
{
|
||||
uid->prefs = cdk_calloc (1, sizeof (*uid->prefs) * (n + 1));
|
||||
if (!uid->prefs)
|
||||
return CDK_Out_Of_Core;
|
||||
n = 0;
|
||||
for (; nsymalg; nsymalg--, n++)
|
||||
{
|
||||
uid->prefs[n].type = CDK_PREFTYPE_SYM;
|
||||
uid->prefs[n].value = *symalg++;
|
||||
}
|
||||
for (; nhashalg; nhashalg--, n++)
|
||||
{
|
||||
uid->prefs[n].type = CDK_PREFTYPE_HASH;
|
||||
uid->prefs[n].value = *hashalg++;
|
||||
}
|
||||
for (; ncompalg; ncompalg--, n++)
|
||||
{
|
||||
uid->prefs[n].type = CDK_PREFTYPE_ZIP;
|
||||
uid->prefs[n].value = *compalg++;
|
||||
}
|
||||
|
||||
uid->prefs[n].type = CDK_PREFTYPE_NONE; /* end of list marker */
|
||||
uid->prefs[n].value = 0;
|
||||
uid->prefs_size = n;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now we add the extracted information to the primary key. */
|
||||
kbnode = cdk_kbnode_find (key, CDK_PKT_PUBLIC_KEY);
|
||||
if (kbnode)
|
||||
{
|
||||
cdk_pkt_pubkey_t pk = kbnode->pkt->pkt.public_key;
|
||||
if (uid && uid->prefs && n)
|
||||
{
|
||||
if (pk->prefs != NULL)
|
||||
cdk_free (pk->prefs);
|
||||
pk->prefs = _cdk_copy_prefs (uid->prefs);
|
||||
pk->prefs_size = n;
|
||||
}
|
||||
if (key_expire)
|
||||
{
|
||||
pk->expiredate = pk->timestamp + key_expire;
|
||||
pk->has_expired = pk->expiredate > (u32) time (NULL) ? 0 : 1;
|
||||
}
|
||||
|
||||
if (key_usage)
|
||||
pk->pubkey_usage = key_usage;
|
||||
pk->is_invalid = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
keydb_parse_allsigs (cdk_kbnode_t knode, cdk_keydb_hd_t hd, int check)
|
||||
{
|
||||
cdk_kbnode_t node, kb;
|
||||
cdk_pkt_signature_t sig;
|
||||
cdk_pkt_pubkey_t pk;
|
||||
cdk_subpkt_t s = NULL;
|
||||
u32 expiredate = 0, curtime = (u32) time (NULL);
|
||||
u32 keyid[2];
|
||||
|
||||
if (!knode)
|
||||
return CDK_Inv_Value;
|
||||
if (check && !hd)
|
||||
return CDK_Inv_Mode;
|
||||
|
||||
kb = cdk_kbnode_find (knode, CDK_PKT_SECRET_KEY);
|
||||
if (kb)
|
||||
return 0;
|
||||
|
||||
/* Reset */
|
||||
for (node = knode; node; node = node->next)
|
||||
{
|
||||
if (node->pkt->pkttype == CDK_PKT_USER_ID)
|
||||
node->pkt->pkt.user_id->is_revoked = 0;
|
||||
else if (node->pkt->pkttype == CDK_PKT_PUBLIC_KEY ||
|
||||
node->pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY)
|
||||
node->pkt->pkt.public_key->is_revoked = 0;
|
||||
}
|
||||
|
||||
kb = cdk_kbnode_find (knode, CDK_PKT_PUBLIC_KEY);
|
||||
if (!kb)
|
||||
return CDK_Wrong_Format;
|
||||
cdk_pk_get_keyid (kb->pkt->pkt.public_key, keyid);
|
||||
|
||||
for (node = knode; node; node = node->next)
|
||||
{
|
||||
if (node->pkt->pkttype == CDK_PKT_SIGNATURE)
|
||||
{
|
||||
sig = node->pkt->pkt.signature;
|
||||
/* Revocation certificates for primary keys */
|
||||
if (sig->sig_class == 0x20)
|
||||
{
|
||||
kb = cdk_kbnode_find_prev (knode, node, CDK_PKT_PUBLIC_KEY);
|
||||
if (kb)
|
||||
{
|
||||
kb->pkt->pkt.public_key->is_revoked = 1;
|
||||
if (check)
|
||||
_cdk_pk_check_sig (hd, kb, node, NULL);
|
||||
}
|
||||
else
|
||||
return CDK_Error_No_Key;
|
||||
}
|
||||
/* Revocation certificates for subkeys */
|
||||
else if (sig->sig_class == 0x28)
|
||||
{
|
||||
kb = cdk_kbnode_find_prev (knode, node, CDK_PKT_PUBLIC_SUBKEY);
|
||||
if (kb)
|
||||
{
|
||||
kb->pkt->pkt.public_key->is_revoked = 1;
|
||||
if (check)
|
||||
_cdk_pk_check_sig (hd, kb, node, NULL);
|
||||
}
|
||||
else
|
||||
return CDK_Error_No_Key;
|
||||
}
|
||||
/* Revocation certifcates for user ID's */
|
||||
else if (sig->sig_class == 0x30)
|
||||
{
|
||||
if (sig->keyid[0] != keyid[0] || sig->keyid[1] != keyid[1])
|
||||
continue; /* revokes an earlier signature, no userID. */
|
||||
kb = cdk_kbnode_find_prev (knode, node, CDK_PKT_USER_ID);
|
||||
if (kb)
|
||||
{
|
||||
kb->pkt->pkt.user_id->is_revoked = 1;
|
||||
if (check)
|
||||
_cdk_pk_check_sig (hd, kb, node, NULL);
|
||||
}
|
||||
else
|
||||
return CDK_Error_No_Key;
|
||||
}
|
||||
/* Direct certificates for primary keys */
|
||||
else if (sig->sig_class == 0x1F)
|
||||
{
|
||||
kb = cdk_kbnode_find_prev (knode, node, CDK_PKT_PUBLIC_KEY);
|
||||
if (kb)
|
||||
{
|
||||
pk = kb->pkt->pkt.public_key;
|
||||
pk->is_invalid = 0;
|
||||
s = cdk_subpkt_find (node->pkt->pkt.signature->hashed,
|
||||
CDK_SIGSUBPKT_KEY_EXPIRE);
|
||||
if (s)
|
||||
{
|
||||
expiredate = _cdk_buftou32 (s->d);
|
||||
pk->expiredate = pk->timestamp + expiredate;
|
||||
pk->has_expired = pk->expiredate > curtime ? 0 : 1;
|
||||
}
|
||||
if (check)
|
||||
_cdk_pk_check_sig (hd, kb, node, NULL);
|
||||
}
|
||||
else
|
||||
return CDK_Error_No_Key;
|
||||
}
|
||||
/* Direct certificates for subkeys */
|
||||
else if (sig->sig_class == 0x18)
|
||||
{
|
||||
kb = cdk_kbnode_find_prev (knode, node, CDK_PKT_PUBLIC_SUBKEY);
|
||||
if (kb)
|
||||
{
|
||||
pk = kb->pkt->pkt.public_key;
|
||||
pk->is_invalid = 0;
|
||||
s = cdk_subpkt_find (node->pkt->pkt.signature->hashed,
|
||||
CDK_SIGSUBPKT_KEY_EXPIRE);
|
||||
if (s)
|
||||
{
|
||||
expiredate = _cdk_buftou32 (s->d);
|
||||
pk->expiredate = pk->timestamp + expiredate;
|
||||
pk->has_expired = pk->expiredate > curtime ? 0 : 1;
|
||||
}
|
||||
if (check)
|
||||
_cdk_pk_check_sig (hd, kb, node, NULL);
|
||||
}
|
||||
else
|
||||
return CDK_Error_No_Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
node = cdk_kbnode_find (knode, CDK_PKT_PUBLIC_KEY);
|
||||
if (node && node->pkt->pkt.public_key->version == 3)
|
||||
{
|
||||
/* v3 public keys have no additonal signatures for the key directly.
|
||||
we say the key is valid when we have at least a self signature. */
|
||||
pk = node->pkt->pkt.public_key;
|
||||
for (node = knode; node; node = node->next)
|
||||
{
|
||||
if (is_selfsig (node, keyid))
|
||||
{
|
||||
pk->is_invalid = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node && (node->pkt->pkt.public_key->is_revoked ||
|
||||
node->pkt->pkt.public_key->has_expired))
|
||||
{
|
||||
/* If the primary key has been revoked, mark all subkeys as invalid
|
||||
because without a primary key they are not useable */
|
||||
for (node = knode; node; node = node->next)
|
||||
{
|
||||
if (node->pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY)
|
||||
node->pkt->pkt.public_key->is_invalid = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
cdk_keydb_get_keyblock (cdk_stream_t inp, cdk_kbnode_t * r_knode)
|
||||
{
|
||||
cdk_packet_t pkt;
|
||||
cdk_kbnode_t knode, node;
|
||||
cdk_desig_revoker_t revkeys;
|
||||
cdk_error_t rc;
|
||||
u32 keyid[2], main_keyid[2];
|
||||
off_t old_off;
|
||||
int key_seen, got_key;
|
||||
|
||||
if (!inp || !r_knode)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
/* Reset all values. */
|
||||
keyid[0] = keyid[1] = 0;
|
||||
main_keyid[0] = main_keyid[1] = 0;
|
||||
revkeys = NULL;
|
||||
knode = NULL;
|
||||
key_seen = got_key = 0;
|
||||
|
||||
*r_knode = NULL;
|
||||
rc = CDK_EOF;
|
||||
while (!cdk_stream_eof (inp))
|
||||
{
|
||||
cdk_pkt_new (&pkt);
|
||||
old_off = cdk_stream_tell (inp);
|
||||
rc = cdk_pkt_read (inp, pkt);
|
||||
if (rc)
|
||||
{
|
||||
cdk_pkt_release (pkt);
|
||||
if (rc == CDK_EOF)
|
||||
break;
|
||||
else
|
||||
{ /* Release all packets we reached so far. */
|
||||
_cdk_log_debug ("keydb_get_keyblock: error %d\n", rc);
|
||||
cdk_kbnode_release (knode);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
if (pkt->pkttype == CDK_PKT_PUBLIC_KEY ||
|
||||
pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY ||
|
||||
pkt->pkttype == CDK_PKT_SECRET_KEY ||
|
||||
pkt->pkttype == CDK_PKT_SECRET_SUBKEY)
|
||||
{
|
||||
if (key_seen && (pkt->pkttype == CDK_PKT_PUBLIC_KEY ||
|
||||
pkt->pkttype == CDK_PKT_SECRET_KEY))
|
||||
{
|
||||
/* The next key starts here so set the file pointer
|
||||
and leave the loop. */
|
||||
cdk_stream_seek (inp, old_off);
|
||||
cdk_pkt_release (pkt);
|
||||
break;
|
||||
}
|
||||
if (pkt->pkttype == CDK_PKT_PUBLIC_KEY ||
|
||||
pkt->pkttype == CDK_PKT_SECRET_KEY)
|
||||
{
|
||||
_cdk_pkt_get_keyid (pkt, main_keyid);
|
||||
key_seen = 1;
|
||||
}
|
||||
else if (pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY ||
|
||||
pkt->pkttype == CDK_PKT_SECRET_SUBKEY)
|
||||
{
|
||||
if (pkt->pkttype == CDK_PKT_PUBLIC_SUBKEY)
|
||||
{
|
||||
pkt->pkt.public_key->main_keyid[0] = main_keyid[0];
|
||||
pkt->pkt.public_key->main_keyid[1] = main_keyid[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
pkt->pkt.secret_key->main_keyid[0] = main_keyid[0];
|
||||
pkt->pkt.secret_key->main_keyid[1] = main_keyid[1];
|
||||
}
|
||||
}
|
||||
/* We save this for the signature */
|
||||
_cdk_pkt_get_keyid (pkt, keyid);
|
||||
got_key = 1;
|
||||
}
|
||||
else if (pkt->pkttype == CDK_PKT_USER_ID)
|
||||
;
|
||||
else if (pkt->pkttype == CDK_PKT_SIGNATURE)
|
||||
{
|
||||
pkt->pkt.signature->key[0] = keyid[0];
|
||||
pkt->pkt.signature->key[1] = keyid[1];
|
||||
if (pkt->pkt.signature->sig_class == 0x1F &&
|
||||
pkt->pkt.signature->revkeys)
|
||||
revkeys = pkt->pkt.signature->revkeys;
|
||||
}
|
||||
node = cdk_kbnode_new (pkt);
|
||||
if (!knode)
|
||||
knode = node;
|
||||
else
|
||||
_cdk_kbnode_add (knode, node);
|
||||
}
|
||||
|
||||
if (got_key)
|
||||
{
|
||||
keydb_merge_selfsig (knode, main_keyid);
|
||||
rc = keydb_parse_allsigs (knode, NULL, 0);
|
||||
if (revkeys)
|
||||
{
|
||||
node = cdk_kbnode_find (knode, CDK_PKT_PUBLIC_KEY);
|
||||
if (node)
|
||||
node->pkt->pkt.public_key->revkeys = revkeys;
|
||||
}
|
||||
}
|
||||
else
|
||||
cdk_kbnode_release (knode);
|
||||
*r_knode = got_key ? knode : NULL;
|
||||
|
||||
/* It is possible that we are in an EOF condition after we
|
||||
successfully read a keyblock. For example if the requested
|
||||
key is the last in the file. */
|
||||
if (rc == CDK_EOF && got_key)
|
||||
rc = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Return the type of the given data. In case it cannot be classified,
|
||||
a substring search will be performed. */
|
||||
static int
|
||||
classify_data (const byte * buf, size_t len)
|
||||
{
|
||||
int type;
|
||||
int i;
|
||||
|
||||
if (buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X'))
|
||||
{ /* Skip hex prefix. */
|
||||
buf += 2;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
/* The length of the data does not match either a keyid or a fingerprint. */
|
||||
if (len != 8 && len != 16 && len != 40)
|
||||
return CDK_DBSEARCH_SUBSTR;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (!isxdigit (buf[i]))
|
||||
return CDK_DBSEARCH_SUBSTR;
|
||||
}
|
||||
if (i != len)
|
||||
return CDK_DBSEARCH_SUBSTR;
|
||||
switch (len)
|
||||
{
|
||||
case 8:
|
||||
type = CDK_DBSEARCH_SHORT_KEYID;
|
||||
break;
|
||||
case 16:
|
||||
type = CDK_DBSEARCH_KEYID;
|
||||
break;
|
||||
case 40:
|
||||
type = CDK_DBSEARCH_FPR;
|
||||
break;
|
||||
default:
|
||||
type = CDK_DBSEARCH_SUBSTR;
|
||||
break;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_keydb_export:
|
||||
* @hd: the keydb handle
|
||||
* @out: the output stream
|
||||
* @remusr: the list of key pattern to export
|
||||
*
|
||||
* Export a list of keys to the given output stream.
|
||||
* Use string list with names for pattering searching.
|
||||
* This procedure strips local signatures.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_keydb_export (cdk_keydb_hd_t hd, cdk_stream_t out, cdk_strlist_t remusr)
|
||||
{
|
||||
cdk_kbnode_t knode, node;
|
||||
cdk_strlist_t r;
|
||||
cdk_error_t rc;
|
||||
int old_ctb;
|
||||
|
||||
for (r = remusr; r; r = r->next)
|
||||
{
|
||||
rc = cdk_keydb_search_start (hd, CDK_DBSEARCH_AUTO, r->d);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = cdk_keydb_search (hd, &knode);
|
||||
if (rc)
|
||||
return rc;
|
||||
node = cdk_kbnode_find (knode, CDK_PKT_PUBLIC_KEY);
|
||||
if (!node)
|
||||
return CDK_Error_No_Key;
|
||||
|
||||
/* If the key is a version 3 key, use the old packet
|
||||
format for the output. */
|
||||
if (node->pkt->pkt.public_key->version == 3)
|
||||
old_ctb = 1;
|
||||
else
|
||||
old_ctb = 0;
|
||||
|
||||
for (node = knode; node; node = node->next)
|
||||
{
|
||||
/* No specified format; skip them */
|
||||
if (node->pkt->pkttype == CDK_PKT_RING_TRUST)
|
||||
continue;
|
||||
/* We never export local signed signatures */
|
||||
if (node->pkt->pkttype == CDK_PKT_SIGNATURE &&
|
||||
!node->pkt->pkt.signature->flags.exportable)
|
||||
continue;
|
||||
/* Filter out invalid signatures */
|
||||
if (node->pkt->pkttype == CDK_PKT_SIGNATURE &&
|
||||
!KEY_CAN_SIGN (node->pkt->pkt.signature->pubkey_algo))
|
||||
continue;
|
||||
|
||||
/* Adjust the ctb flag if needed. */
|
||||
node->pkt->old_ctb = old_ctb;
|
||||
rc = cdk_pkt_write (out, node->pkt);
|
||||
if (rc)
|
||||
{
|
||||
cdk_kbnode_release (knode);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
cdk_kbnode_release (knode);
|
||||
knode = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_packet_t
|
||||
find_key_packet (cdk_kbnode_t knode, int *r_is_sk)
|
||||
{
|
||||
cdk_packet_t pkt;
|
||||
|
||||
pkt = cdk_kbnode_find_packet (knode, CDK_PKT_PUBLIC_KEY);
|
||||
if (!pkt)
|
||||
{
|
||||
pkt = cdk_kbnode_find_packet (knode, CDK_PKT_SECRET_KEY);
|
||||
if (r_is_sk)
|
||||
*r_is_sk = pkt ? 1 : 0;
|
||||
}
|
||||
return pkt;
|
||||
}
|
||||
|
||||
|
||||
/* Return 1 if the is allowd in a key node. */
|
||||
static int
|
||||
is_key_node (cdk_kbnode_t node)
|
||||
{
|
||||
switch (node->pkt->pkttype)
|
||||
{
|
||||
case CDK_PKT_SIGNATURE:
|
||||
case CDK_PKT_SECRET_KEY:
|
||||
case CDK_PKT_PUBLIC_KEY:
|
||||
case CDK_PKT_SECRET_SUBKEY:
|
||||
case CDK_PKT_PUBLIC_SUBKEY:
|
||||
case CDK_PKT_USER_ID:
|
||||
case CDK_PKT_ATTRIBUTE:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
cdk_keydb_import (cdk_keydb_hd_t hd, cdk_kbnode_t knode)
|
||||
{
|
||||
cdk_kbnode_t node, chk;
|
||||
cdk_packet_t pkt;
|
||||
cdk_stream_t out;
|
||||
cdk_error_t rc;
|
||||
u32 keyid[2];
|
||||
|
||||
if (!hd || !knode)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
pkt = find_key_packet (knode, NULL);
|
||||
if (!pkt)
|
||||
return CDK_Inv_Packet;
|
||||
|
||||
_cdk_pkt_get_keyid (pkt, keyid);
|
||||
chk = NULL;
|
||||
cdk_keydb_get_bykeyid (hd, keyid, &chk);
|
||||
if (chk)
|
||||
{ /* FIXME: search for new signatures */
|
||||
cdk_kbnode_release (chk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We append data to the stream so we need to close
|
||||
the stream here to re-open it later. */
|
||||
if (hd->fp)
|
||||
{
|
||||
cdk_stream_close (hd->fp);
|
||||
hd->fp = NULL;
|
||||
}
|
||||
|
||||
rc = _cdk_stream_append (hd->name, &out);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
for (node = knode; node; node = node->next)
|
||||
{
|
||||
if (node->pkt->pkttype == CDK_PKT_RING_TRUST)
|
||||
continue; /* No uniformed syntax for this packet */
|
||||
if (node->pkt->pkttype == CDK_PKT_SIGNATURE &&
|
||||
!node->pkt->pkt.signature->flags.exportable)
|
||||
{
|
||||
_cdk_log_debug ("key db import: skip local signature\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_key_node (node))
|
||||
{
|
||||
_cdk_log_debug ("key db import: skip invalid node of type %d\n",
|
||||
node->pkt->pkttype);
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = cdk_pkt_write (out, node->pkt);
|
||||
if (rc)
|
||||
{
|
||||
cdk_stream_close (out);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
cdk_stream_close (out);
|
||||
if (!hd->no_cache)
|
||||
cdk_keydb_idx_rebuild (hd);
|
||||
hd->stats.new_keys++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
_cdk_keydb_check_userid (cdk_keydb_hd_t hd, u32 * keyid, const char *id)
|
||||
{
|
||||
cdk_kbnode_t knode = NULL, unode = NULL;
|
||||
cdk_error_t rc;
|
||||
int check;
|
||||
|
||||
if (!hd)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
rc = cdk_keydb_search_start (hd, CDK_DBSEARCH_KEYID, keyid);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = cdk_keydb_search (hd, &knode);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = cdk_keydb_search_start (hd, CDK_DBSEARCH_EXACT, (char *) id);
|
||||
if (!rc)
|
||||
rc = cdk_keydb_search (hd, &unode);
|
||||
if (rc)
|
||||
{
|
||||
cdk_kbnode_release (knode);
|
||||
return rc;
|
||||
}
|
||||
|
||||
check = 0;
|
||||
cdk_keydb_search_start (hd, CDK_DBSEARCH_KEYID, keyid);
|
||||
if (unode && find_by_keyid (unode, hd->dbs))
|
||||
check++;
|
||||
cdk_kbnode_release (unode);
|
||||
|
||||
cdk_keydb_search_start (hd, CDK_DBSEARCH_EXACT, (char *) id);
|
||||
if (knode && find_by_pattern (knode, hd->dbs))
|
||||
check++;
|
||||
cdk_kbnode_release (knode);
|
||||
|
||||
return check == 2 ? 0 : CDK_Inv_Value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_keydb_check_sk:
|
||||
* @hd: the key db handle
|
||||
* @keyid: the 64-bit keyid
|
||||
*
|
||||
* Check if a secret key with the given key ID is available
|
||||
* in the key database.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_keydb_check_sk (cdk_keydb_hd_t hd, u32 * keyid)
|
||||
{
|
||||
cdk_stream_t db;
|
||||
cdk_packet_t pkt;
|
||||
cdk_error_t rc;
|
||||
u32 kid[2];
|
||||
|
||||
if (!hd || !keyid)
|
||||
return CDK_Inv_Value;
|
||||
if (!hd->secret)
|
||||
return CDK_Inv_Mode;
|
||||
|
||||
rc = _cdk_keydb_open (hd, &db);
|
||||
if (rc)
|
||||
return rc;
|
||||
cdk_pkt_new (&pkt);
|
||||
while (!cdk_pkt_read (db, pkt))
|
||||
{
|
||||
if (pkt->pkttype != CDK_PKT_SECRET_KEY &&
|
||||
pkt->pkttype != CDK_PKT_SECRET_SUBKEY)
|
||||
{
|
||||
cdk_pkt_free (pkt);
|
||||
continue;
|
||||
}
|
||||
cdk_sk_get_keyid (pkt->pkt.secret_key, kid);
|
||||
if (KEYID_CMP (kid, keyid))
|
||||
{
|
||||
cdk_pkt_release (pkt);
|
||||
return 0;
|
||||
}
|
||||
cdk_pkt_free (pkt);
|
||||
}
|
||||
cdk_pkt_release (pkt);
|
||||
return CDK_Error_No_Key;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_listkey_start:
|
||||
* @r_ctx: pointer to store the new context
|
||||
* @db: the key database handle
|
||||
* @patt: string pattern
|
||||
* @fpatt: recipients from a stringlist to show
|
||||
*
|
||||
* Prepare a key listing with the given parameters. Two modes are supported.
|
||||
* The first mode uses string pattern to determine if the key should be
|
||||
* returned or not. The other mode uses a string list to request the key
|
||||
* which should be listed.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_listkey_start (cdk_listkey_t * r_ctx, cdk_keydb_hd_t db,
|
||||
const char *patt, cdk_strlist_t fpatt)
|
||||
{
|
||||
cdk_listkey_t ctx;
|
||||
cdk_stream_t inp;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!r_ctx || !db)
|
||||
return CDK_Inv_Value;
|
||||
if ((patt && fpatt) || (!patt && !fpatt))
|
||||
return CDK_Inv_Mode;
|
||||
rc = _cdk_keydb_open (db, &inp);
|
||||
if (rc)
|
||||
return rc;
|
||||
ctx = cdk_calloc (1, sizeof *ctx);
|
||||
if (!ctx)
|
||||
return CDK_Out_Of_Core;
|
||||
ctx->db = db;
|
||||
ctx->inp = inp;
|
||||
if (patt)
|
||||
{
|
||||
ctx->u.patt = cdk_strdup (patt);
|
||||
if (!ctx->u.patt)
|
||||
return CDK_Out_Of_Core;
|
||||
}
|
||||
else if (fpatt)
|
||||
{
|
||||
cdk_strlist_t l;
|
||||
for (l = fpatt; l; l = l->next)
|
||||
cdk_strlist_add (&ctx->u.fpatt, l->d);
|
||||
}
|
||||
ctx->type = patt ? 1 : 0;
|
||||
ctx->init = 1;
|
||||
*r_ctx = ctx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_listkey_close:
|
||||
* @ctx: the list key context
|
||||
*
|
||||
* Free the list key context.
|
||||
**/
|
||||
void
|
||||
cdk_listkey_close (cdk_listkey_t ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
if (ctx->type)
|
||||
cdk_free (ctx->u.patt);
|
||||
else
|
||||
cdk_strlist_free (ctx->u.fpatt);
|
||||
cdk_free (ctx);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_listkey_next:
|
||||
* @ctx: list key context
|
||||
* @r_key: the pointer to the new key node object
|
||||
*
|
||||
* Retrieve the next key from the pattern of the key list context.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_listkey_next (cdk_listkey_t ctx, cdk_kbnode_t * ret_key)
|
||||
{
|
||||
if (!ctx || !ret_key)
|
||||
return CDK_Inv_Value;
|
||||
if (!ctx->init)
|
||||
return CDK_Inv_Mode;
|
||||
|
||||
if (ctx->type && ctx->u.patt[0] == '*')
|
||||
return cdk_keydb_get_keyblock (ctx->inp, ret_key);
|
||||
else if (ctx->type)
|
||||
{
|
||||
cdk_kbnode_t node;
|
||||
struct cdk_dbsearch_s ks;
|
||||
cdk_error_t rc;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
rc = cdk_keydb_get_keyblock (ctx->inp, &node);
|
||||
if (rc)
|
||||
return rc;
|
||||
memset (&ks, 0, sizeof (ks));
|
||||
ks.type = CDK_DBSEARCH_SUBSTR;
|
||||
ks.u.pattern = ctx->u.patt;
|
||||
if (find_by_pattern (node, &ks))
|
||||
{
|
||||
*ret_key = node;
|
||||
return 0;
|
||||
}
|
||||
cdk_kbnode_release (node);
|
||||
node = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ctx->t)
|
||||
ctx->t = ctx->u.fpatt;
|
||||
else if (ctx->t->next)
|
||||
ctx->t = ctx->t->next;
|
||||
else
|
||||
return CDK_EOF;
|
||||
return cdk_keydb_get_bypattern (ctx->db, ctx->t->d, ret_key);
|
||||
}
|
||||
return CDK_General_Error;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
_cdk_keydb_is_secret (cdk_keydb_hd_t db)
|
||||
{
|
||||
return db->secret;
|
||||
}
|
||||
@@ -1,307 +0,0 @@
|
||||
/* Literal.c - Literal packet filters
|
||||
* Copyright (C) 2002, 2003 Timo Schulz
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "opencdk.h"
|
||||
#include "main.h"
|
||||
#include "filters.h"
|
||||
|
||||
|
||||
/* Duplicate the string @s but strip of possible
|
||||
relative folder names of it. */
|
||||
static char *
|
||||
dup_trim_filename (const char *s)
|
||||
{
|
||||
char *p = NULL;
|
||||
|
||||
p = strrchr (s, '/');
|
||||
if (!p)
|
||||
p = strrchr (s, '\\');
|
||||
if (!p)
|
||||
return cdk_strdup (s);
|
||||
return cdk_strdup (p + 1);
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
literal_decode (void *opaque, FILE * in, FILE * out)
|
||||
{
|
||||
literal_filter_t *pfx = opaque;
|
||||
cdk_stream_t si, so;
|
||||
cdk_packet_t pkt;
|
||||
cdk_pkt_literal_t pt;
|
||||
byte buf[BUFSIZE];
|
||||
size_t nread;
|
||||
int bufsize;
|
||||
cdk_error_t rc;
|
||||
|
||||
_cdk_log_debug ("literal filter: decode\n");
|
||||
|
||||
if (!pfx || !in || !out)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
rc = _cdk_stream_fpopen (in, STREAMCTL_READ, &si);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
cdk_pkt_new (&pkt);
|
||||
rc = cdk_pkt_read (si, pkt);
|
||||
if (rc || pkt->pkttype != CDK_PKT_LITERAL)
|
||||
{
|
||||
cdk_pkt_release (pkt);
|
||||
cdk_stream_close (si);
|
||||
return !rc ? CDK_Inv_Packet : rc;
|
||||
}
|
||||
|
||||
rc = _cdk_stream_fpopen (out, STREAMCTL_WRITE, &so);
|
||||
if (rc)
|
||||
{
|
||||
cdk_pkt_release (pkt);
|
||||
cdk_stream_close (si);
|
||||
return rc;
|
||||
}
|
||||
|
||||
pt = pkt->pkt.literal;
|
||||
pfx->mode = pt->mode;
|
||||
|
||||
if (pfx->filename && pt->namelen > 0)
|
||||
{
|
||||
/* The name in the literal packet is more authorative. */
|
||||
cdk_free (pfx->filename);
|
||||
pfx->filename = dup_trim_filename (pt->name);
|
||||
}
|
||||
else if (!pfx->filename && pt->namelen > 0)
|
||||
pfx->filename = dup_trim_filename (pt->name);
|
||||
else if (!pt->namelen && !pfx->filename && pfx->orig_filename)
|
||||
{
|
||||
/* In this case, we need to derrive the output file name
|
||||
from the original name and cut off the OpenPGP extension.
|
||||
If this is not possible, we return an error. */
|
||||
if (!stristr (pfx->orig_filename, ".gpg") &&
|
||||
!stristr (pfx->orig_filename, ".pgp") &&
|
||||
!stristr (pfx->orig_filename, ".asc"))
|
||||
{
|
||||
cdk_pkt_release (pkt);
|
||||
cdk_stream_close (si);
|
||||
cdk_stream_close (so);
|
||||
_cdk_log_debug
|
||||
("literal filter: no file name and no PGP extension\n");
|
||||
return CDK_Inv_Mode;
|
||||
}
|
||||
_cdk_log_debug ("literal filter: derrive file name from original\n");
|
||||
pfx->filename = dup_trim_filename (pfx->orig_filename);
|
||||
pfx->filename[strlen (pfx->filename) - 4] = '\0';
|
||||
}
|
||||
|
||||
while (!feof (in))
|
||||
{
|
||||
_cdk_log_debug ("literal_decode: part on %d size %lu\n",
|
||||
pfx->blkmode.on, pfx->blkmode.size);
|
||||
if (pfx->blkmode.on)
|
||||
bufsize = pfx->blkmode.size;
|
||||
else
|
||||
bufsize = pt->len < DIM (buf) ? pt->len : DIM (buf);
|
||||
nread = cdk_stream_read (pt->buf, buf, bufsize);
|
||||
if (nread == EOF)
|
||||
{
|
||||
rc = CDK_File_Error;
|
||||
break;
|
||||
}
|
||||
if (pfx->md)
|
||||
gcry_md_write (pfx->md, buf, nread);
|
||||
cdk_stream_write (so, buf, nread);
|
||||
pt->len -= nread;
|
||||
if (pfx->blkmode.on)
|
||||
{
|
||||
pfx->blkmode.size = _cdk_pkt_read_len (in, &pfx->blkmode.on);
|
||||
if (pfx->blkmode.size == (size_t) EOF)
|
||||
return CDK_Inv_Packet;
|
||||
}
|
||||
if (pt->len <= 0 && !pfx->blkmode.on)
|
||||
break;
|
||||
}
|
||||
|
||||
cdk_stream_close (si);
|
||||
cdk_stream_close (so);
|
||||
cdk_pkt_release (pkt);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static char
|
||||
intmode_to_char (int mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case CDK_LITFMT_BINARY:
|
||||
return 'b';
|
||||
case CDK_LITFMT_TEXT:
|
||||
return 't';
|
||||
case CDK_LITFMT_UNICODE:
|
||||
return 'u';
|
||||
default:
|
||||
return 'b';
|
||||
}
|
||||
|
||||
return 'b';
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
literal_encode (void *opaque, FILE * in, FILE * out)
|
||||
{
|
||||
literal_filter_t *pfx = opaque;
|
||||
cdk_pkt_literal_t pt;
|
||||
cdk_stream_t si;
|
||||
cdk_packet_t pkt;
|
||||
size_t filelen;
|
||||
cdk_error_t rc;
|
||||
|
||||
_cdk_log_debug ("literal filter: encode\n");
|
||||
|
||||
if (!pfx || !in || !out)
|
||||
return CDK_Inv_Value;
|
||||
if (!pfx->filename)
|
||||
{
|
||||
pfx->filename = cdk_strdup ("_CONSOLE");
|
||||
if (!pfx->filename)
|
||||
return CDK_Out_Of_Core;
|
||||
}
|
||||
|
||||
rc = _cdk_stream_fpopen (in, STREAMCTL_READ, &si);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
filelen = strlen (pfx->filename);
|
||||
cdk_pkt_new (&pkt);
|
||||
pt = pkt->pkt.literal = cdk_calloc (1, sizeof *pt + filelen - 1);
|
||||
if (!pt)
|
||||
{
|
||||
cdk_pkt_release (pkt);
|
||||
cdk_stream_close (si);
|
||||
return CDK_Out_Of_Core;
|
||||
}
|
||||
memcpy (pt->name, pfx->filename, filelen);
|
||||
pt->namelen = filelen;
|
||||
pt->name[pt->namelen] = '\0';
|
||||
pt->timestamp = (u32) time (NULL);
|
||||
pt->mode = intmode_to_char (pfx->mode);
|
||||
pt->len = cdk_stream_get_length (si);
|
||||
pt->buf = si;
|
||||
pkt->old_ctb = 1;
|
||||
pkt->pkttype = CDK_PKT_LITERAL;
|
||||
pkt->pkt.literal = pt;
|
||||
rc = _cdk_pkt_write_fp (out, pkt);
|
||||
|
||||
cdk_pkt_release (pkt);
|
||||
cdk_stream_close (si);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
_cdk_filter_literal (void *opaque, int ctl, FILE * in, FILE * out)
|
||||
{
|
||||
if (ctl == STREAMCTL_READ)
|
||||
return literal_decode (opaque, in, out);
|
||||
else if (ctl == STREAMCTL_WRITE)
|
||||
return literal_encode (opaque, in, out);
|
||||
else if (ctl == STREAMCTL_FREE)
|
||||
{
|
||||
literal_filter_t *pfx = opaque;
|
||||
if (pfx)
|
||||
{
|
||||
_cdk_log_debug ("free literal filter\n");
|
||||
cdk_free (pfx->filename);
|
||||
pfx->filename = NULL;
|
||||
cdk_free (pfx->orig_filename);
|
||||
pfx->orig_filename = NULL;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return CDK_Inv_Mode;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
text_encode (void *opaque, FILE * in, FILE * out)
|
||||
{
|
||||
const char *s;
|
||||
char buf[2048];
|
||||
|
||||
if (!in || !out)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
/* FIXME: This code does not work for very long lines. */
|
||||
while (!feof (in))
|
||||
{
|
||||
s = fgets (buf, DIM (buf) - 1, in);
|
||||
if (!s)
|
||||
break;
|
||||
_cdk_trim_string (buf, 1);
|
||||
fwrite (buf, 1, strlen (buf), out);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
text_decode (void *opaque, FILE * in, FILE * out)
|
||||
{
|
||||
text_filter_t *tfx = opaque;
|
||||
const char *s;
|
||||
char buf[2048];
|
||||
|
||||
if (!tfx || !in || !out)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
while (!feof (in))
|
||||
{
|
||||
s = fgets (buf, DIM (buf) - 1, in);
|
||||
if (!s)
|
||||
break;
|
||||
_cdk_trim_string (buf, 0);
|
||||
fwrite (buf, 1, strlen (buf), out);
|
||||
fwrite (tfx->lf, 1, strlen (tfx->lf), out);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
_cdk_filter_text (void *opaque, int ctl, FILE * in, FILE * out)
|
||||
{
|
||||
if (ctl == STREAMCTL_READ)
|
||||
return text_encode (opaque, in, out);
|
||||
else if (ctl == STREAMCTL_WRITE)
|
||||
return text_decode (opaque, in, out);
|
||||
else if (ctl == STREAMCTL_FREE)
|
||||
{
|
||||
text_filter_t *tfx = opaque;
|
||||
if (tfx)
|
||||
{
|
||||
_cdk_log_debug ("free text filter\n");
|
||||
tfx->lf = NULL;
|
||||
}
|
||||
}
|
||||
return CDK_Inv_Mode;
|
||||
}
|
||||
@@ -1,779 +0,0 @@
|
||||
/* main.c
|
||||
* Copyright (C) 2001, 2002, 2003, 2007 Timo Schulz
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "opencdk.h"
|
||||
#include "main.h"
|
||||
#include "packet.h"
|
||||
|
||||
|
||||
/* Set a default cipher algorithm and a digest algorithm.
|
||||
Even if AES and SHA-256 are not 'MUST' in the latest
|
||||
OpenPGP draft, AES seems to be a good choice. */
|
||||
#define DEFAULT_CIPHER_ALGO GCRY_CIPHER_AES
|
||||
#define DEFAULT_DIGEST_ALGO GCRY_MD_SHA256
|
||||
|
||||
/* The site of the secure memory which is allocated in gcrypt. */
|
||||
#define SECMEM_SIZE 16384
|
||||
|
||||
|
||||
/* Hooks to custom memory allocation functions. */
|
||||
static void *(*alloc_func) (size_t n) = gcry_xmalloc;
|
||||
static void *(*alloc_secure_func) (size_t n) = gcry_malloc_secure;
|
||||
static void *(*realloc_func) (void *p, size_t n) = gcry_realloc;
|
||||
static void *(*calloc_func) (size_t m, size_t n) = gcry_calloc;
|
||||
static void (*free_func) (void *) = gcry_free;
|
||||
static int malloc_hooks = 0;
|
||||
static int secmem_init = 0;
|
||||
|
||||
/* Global settings for the logging. */
|
||||
static cdk_log_fnc_t log_handler = NULL;
|
||||
static void *log_handler_value = NULL;
|
||||
static int log_level = CDK_LOG_NONE;
|
||||
|
||||
|
||||
/**
|
||||
* cdk_strerror:
|
||||
* @ec: the error number
|
||||
*
|
||||
* Return an error text for the given id.
|
||||
**/
|
||||
const char *
|
||||
cdk_strerror (int ec)
|
||||
{
|
||||
static char buf[20];
|
||||
|
||||
switch (ec)
|
||||
{
|
||||
case CDK_EOF:
|
||||
return "End Of File";
|
||||
case CDK_Success:
|
||||
return "No error";
|
||||
case CDK_General_Error:
|
||||
return "General error";
|
||||
case CDK_File_Error:
|
||||
return strerror (errno);
|
||||
case CDK_Bad_Sig:
|
||||
return "Bad signature";
|
||||
case CDK_Inv_Packet:
|
||||
return "Invalid packet";
|
||||
case CDK_Inv_Algo:
|
||||
return "Invalid algorithm";
|
||||
case CDK_Not_Implemented:
|
||||
return "This is not implemented yet";
|
||||
case CDK_Armor_Error:
|
||||
return "ASCII armor error";
|
||||
case CDK_Armor_CRC_Error:
|
||||
return "ASCII armored damaged (CRC error)";
|
||||
case CDK_MPI_Error:
|
||||
return "Invalid or missformed MPI";
|
||||
case CDK_Inv_Value:
|
||||
return "Invalid parameter or value";
|
||||
case CDK_Error_No_Key:
|
||||
return "No key available or not found";
|
||||
case CDK_Chksum_Error:
|
||||
return "Check for key does not match";
|
||||
case CDK_Time_Conflict:
|
||||
return "Time conflict";
|
||||
case CDK_Zlib_Error:
|
||||
return "ZLIB error";
|
||||
case CDK_Weak_Key:
|
||||
return "Weak key was detected";
|
||||
case CDK_Out_Of_Core:
|
||||
return "Out of core!!";
|
||||
case CDK_Wrong_Seckey:
|
||||
return "Wrong secret key";
|
||||
case CDK_Wrong_Format:
|
||||
return "Data has wrong format";
|
||||
case CDK_Bad_MDC:
|
||||
return "Manipulated MDC detected";
|
||||
case CDK_Inv_Mode:
|
||||
return "Invalid mode";
|
||||
case CDK_Error_No_Keyring:
|
||||
return "No keyring available";
|
||||
case CDK_Inv_Packet_Ver:
|
||||
return "Invalid version for packet";
|
||||
case CDK_Too_Short:
|
||||
return "Buffer or object is too short";
|
||||
case CDK_Unusable_Key:
|
||||
return "Unusable public key";
|
||||
case CDK_No_Data:
|
||||
return "No data";
|
||||
case CDK_No_Passphrase:
|
||||
return "No passphrase supplied";
|
||||
case CDK_Network_Error:
|
||||
return "A network error occurred";
|
||||
default:
|
||||
sprintf (buf, "ec=%d", ec);
|
||||
return buf;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
out_of_core (size_t n)
|
||||
{
|
||||
fprintf (stderr, "\n ** fatal error: out of memory (%d bytes) **\n", n);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_set_malloc_hooks:
|
||||
* @new_alloc_func: malloc replacement
|
||||
* @new_alloc_secure_func: secure malloc replacement
|
||||
* @new_realloc_func: realloc replacement
|
||||
* @new_calloc_func: calloc replacement
|
||||
* @new_free_func: free replacement
|
||||
*
|
||||
* Set private memory hooks for the library.
|
||||
*/
|
||||
void
|
||||
cdk_set_malloc_hooks (void *(*new_alloc_func) (size_t n),
|
||||
void *(*new_alloc_secure_func) (size_t n),
|
||||
void *(*new_realloc_func) (void *p, size_t n),
|
||||
void *(*new_calloc_func) (size_t m, size_t n),
|
||||
void (*new_free_func) (void *))
|
||||
{
|
||||
alloc_func = new_alloc_func;
|
||||
alloc_secure_func = new_alloc_secure_func;
|
||||
realloc_func = new_realloc_func;
|
||||
calloc_func = new_calloc_func;
|
||||
free_func = new_free_func;
|
||||
malloc_hooks = 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_malloc_hook_initialized:
|
||||
*
|
||||
* Return if the malloc hooks are already initialized.
|
||||
**/
|
||||
int
|
||||
cdk_malloc_hook_initialized (void)
|
||||
{
|
||||
return malloc_hooks;
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
cdk_malloc (size_t size)
|
||||
{
|
||||
void *p = alloc_func (size);
|
||||
if (!p)
|
||||
out_of_core (size);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_calloc:
|
||||
* @n: amount of elements
|
||||
* @m: size of one element
|
||||
*
|
||||
* Safe wrapper around the c-function calloc.
|
||||
**/
|
||||
void *
|
||||
cdk_calloc (size_t n, size_t m)
|
||||
{
|
||||
void *p = calloc_func (n, m);
|
||||
if (!p)
|
||||
out_of_core (m);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/* Things which need to be done after the secure memory initialisation. */
|
||||
static void
|
||||
_secmem_finish (void)
|
||||
{
|
||||
gcry_control (GCRYCTL_DROP_PRIVS);
|
||||
}
|
||||
|
||||
|
||||
/* Initialize the secure memory. */
|
||||
static void
|
||||
_secmem_init (size_t size)
|
||||
{
|
||||
if (secmem_init == 1)
|
||||
return;
|
||||
if (size >= SECMEM_SIZE)
|
||||
size = SECMEM_SIZE;
|
||||
|
||||
/* Check if no other library has already initialized gcrypt. */
|
||||
if (!gcry_control (GCRYCTL_ANY_INITIALIZATION_P))
|
||||
{
|
||||
_cdk_log_debug ("init: libgcrypt initialize.\n");
|
||||
gcry_control (GCRYCTL_INIT_SECMEM, size, 0);
|
||||
gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
|
||||
gcry_control (GCRYCTL_DISABLE_SECMEM_WARN);
|
||||
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, NULL, 0);
|
||||
secmem_init = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Things which needs to be done to deinit the secure memory. */
|
||||
static void
|
||||
_secmem_end (void)
|
||||
{
|
||||
gcry_control (GCRYCTL_TERM_SECMEM);
|
||||
secmem_init = 0;
|
||||
}
|
||||
|
||||
|
||||
/* The Windows system needs to startup the Winsock interface first
|
||||
before we can use any socket related function. */
|
||||
#ifdef _WIN32
|
||||
static void
|
||||
init_sockets (void)
|
||||
{
|
||||
static int initialized = 0;
|
||||
WSADATA wsdata;
|
||||
|
||||
if (initialized)
|
||||
return;
|
||||
if (WSAStartup (0x202, &wsdata))
|
||||
_cdk_log_debug ("winsock init failed.\n");
|
||||
|
||||
initialized = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
deinit_sockets (void)
|
||||
{
|
||||
WSACleanup ();
|
||||
}
|
||||
#else
|
||||
void
|
||||
init_sockets (void)
|
||||
{
|
||||
}
|
||||
void
|
||||
deinit_sockets (void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* cdk_lib_startup:
|
||||
*
|
||||
* Prepare the internal structures of the library.
|
||||
* This function should be called before any other CDK function.
|
||||
*/
|
||||
void
|
||||
cdk_lib_startup (void)
|
||||
{
|
||||
_secmem_init (SECMEM_SIZE);
|
||||
_secmem_finish ();
|
||||
init_sockets ();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_lib_shutdown:
|
||||
*
|
||||
* Shutdown the library and free all internal and globally used
|
||||
* memory and structures. This function should be called in the
|
||||
* exit handler of the calling program.
|
||||
*/
|
||||
void
|
||||
cdk_lib_shutdown (void)
|
||||
{
|
||||
deinit_sockets ();
|
||||
_secmem_end ();
|
||||
}
|
||||
|
||||
/**
|
||||
* cdk_salloc:
|
||||
* @size: how much bytes should be allocated.
|
||||
* @clear: shall the buffer cleared after the allocation?
|
||||
*
|
||||
* Allocated the requested amount of bytes in 'secure' memory.
|
||||
*/
|
||||
void *
|
||||
cdk_salloc (size_t size, int clear)
|
||||
{
|
||||
void *p;
|
||||
|
||||
if (!secmem_init)
|
||||
_secmem_init (SECMEM_SIZE);
|
||||
|
||||
p = alloc_secure_func (size);
|
||||
if (!p)
|
||||
out_of_core (size);
|
||||
if (clear)
|
||||
memset (p, 0, size);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
cdk_realloc (void *ptr, size_t size)
|
||||
{
|
||||
void *p = realloc_func (ptr, size);
|
||||
if (!p)
|
||||
out_of_core (size);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
cdk_strdup (const char *ptr)
|
||||
{
|
||||
char *p = cdk_malloc (strlen (ptr) + 1);
|
||||
if (p)
|
||||
strcpy (p, ptr);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
cdk_free (void *ptr)
|
||||
{
|
||||
if (ptr)
|
||||
free_func (ptr);
|
||||
}
|
||||
|
||||
|
||||
/* Internal logging routine. */
|
||||
static void
|
||||
_cdk_logv (int level, const char *fmt, va_list arg_ptr)
|
||||
{
|
||||
|
||||
if (log_handler)
|
||||
log_handler (log_handler_value, level, fmt, arg_ptr);
|
||||
else
|
||||
{
|
||||
if (level == CDK_LOG_NONE)
|
||||
return;
|
||||
if (level == CDK_LOG_DEBUG)
|
||||
fputs ("DBG: ", stderr);
|
||||
vfprintf (stderr, fmt, arg_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_set_log_handler:
|
||||
* @logfnc: the function pointer
|
||||
* @opaque: a private values for the function
|
||||
*
|
||||
* Set a custom handler for logging.
|
||||
**/
|
||||
void
|
||||
cdk_set_log_handler (cdk_log_fnc_t logfnc, void *opaque)
|
||||
{
|
||||
log_handler = logfnc;
|
||||
log_handler_value = opaque;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_set_log_level:
|
||||
* @lvl: the level
|
||||
*
|
||||
* Set the verbosity level.
|
||||
**/
|
||||
void
|
||||
cdk_set_log_level (int level)
|
||||
{
|
||||
log_level = level;
|
||||
}
|
||||
|
||||
|
||||
/* Return the current log level of the lib. */
|
||||
int
|
||||
_cdk_get_log_level (void)
|
||||
{
|
||||
return log_level;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_cdk_log_info (const char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
|
||||
if (log_level == CDK_LOG_NONE)
|
||||
return;
|
||||
va_start (arg, fmt);
|
||||
_cdk_logv (CDK_LOG_INFO, fmt, arg);
|
||||
va_end (arg);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_cdk_log_debug (const char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
|
||||
if (log_level < CDK_LOG_DEBUG)
|
||||
return;
|
||||
va_start (arg, fmt);
|
||||
_cdk_logv (CDK_LOG_DEBUG, fmt, arg);
|
||||
va_end (arg);
|
||||
}
|
||||
|
||||
|
||||
/* Use the passphrase callback in the handle HD or
|
||||
return NULL if there is no valid callback. */
|
||||
char *
|
||||
_cdk_passphrase_get (cdk_ctx_t hd, const char *prompt)
|
||||
{
|
||||
if (!hd || !hd->passphrase_cb)
|
||||
return NULL;
|
||||
return hd->passphrase_cb (hd->passphrase_cb_value, prompt);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
handle_set_cipher (cdk_ctx_t hd, int cipher)
|
||||
{
|
||||
if (!hd)
|
||||
return;
|
||||
if (gcry_cipher_test_algo (cipher))
|
||||
cipher = DEFAULT_CIPHER_ALGO;
|
||||
hd->cipher_algo = cipher;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
handle_set_digest (cdk_ctx_t hd, int digest)
|
||||
{
|
||||
if (!hd)
|
||||
return;
|
||||
if (gcry_md_test_algo (digest))
|
||||
digest = DEFAULT_DIGEST_ALGO;
|
||||
hd->digest_algo = digest;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
handle_set_s2k (cdk_ctx_t hd, int mode, int digest, int cipher)
|
||||
{
|
||||
if (!hd)
|
||||
return;
|
||||
if (gcry_cipher_test_algo (cipher))
|
||||
cipher = DEFAULT_CIPHER_ALGO;
|
||||
if (gcry_md_test_algo (digest))
|
||||
digest = DEFAULT_DIGEST_ALGO;
|
||||
if (mode != CDK_S2K_SIMPLE &&
|
||||
mode != CDK_S2K_SALTED && mode != CDK_S2K_ITERSALTED)
|
||||
mode = CDK_S2K_ITERSALTED;
|
||||
hd->_s2k.mode = mode;
|
||||
hd->_s2k.digest_algo = digest;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
handle_set_compress (cdk_ctx_t hd, int algo, int level)
|
||||
{
|
||||
if (!hd)
|
||||
return;
|
||||
if (algo < 0 || algo > 2)
|
||||
algo = 0;
|
||||
hd->compress.algo = algo;
|
||||
if (!algo)
|
||||
hd->opt.compress = 0;
|
||||
else
|
||||
{
|
||||
if (level > 0 && level < 10)
|
||||
hd->compress.level = level;
|
||||
else
|
||||
hd->compress.level = 6;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_handle_control:
|
||||
* @hd: session handle
|
||||
* @action: flag which indicates whether put or get is requested
|
||||
* @cmd: command id
|
||||
*
|
||||
* Perform various control operations for the current session.
|
||||
**/
|
||||
int
|
||||
cdk_handle_control (cdk_ctx_t hd, int action, int cmd, ...)
|
||||
{
|
||||
va_list arg_ptr;
|
||||
int set = action == CDK_CTLF_SET, val = 0;
|
||||
|
||||
if (!hd)
|
||||
return -1;
|
||||
|
||||
if (action != CDK_CTLF_SET && action != CDK_CTLF_GET)
|
||||
return -1;
|
||||
va_start (arg_ptr, cmd);
|
||||
switch (cmd)
|
||||
{
|
||||
case CDK_CTL_ARMOR:
|
||||
if (set)
|
||||
hd->opt.armor = va_arg (arg_ptr, int);
|
||||
else
|
||||
val = hd->opt.armor;
|
||||
break;
|
||||
|
||||
case CDK_CTL_CIPHER:
|
||||
if (set)
|
||||
handle_set_cipher (hd, va_arg (arg_ptr, int));
|
||||
else
|
||||
val = hd->cipher_algo;
|
||||
break;
|
||||
|
||||
case CDK_CTL_DIGEST:
|
||||
if (set)
|
||||
handle_set_digest (hd, va_arg (arg_ptr, int));
|
||||
else
|
||||
val = hd->digest_algo;
|
||||
break;
|
||||
|
||||
case CDK_CTL_OVERWRITE:
|
||||
if (set)
|
||||
hd->opt.overwrite = va_arg (arg_ptr, int);
|
||||
else
|
||||
val = hd->opt.overwrite;
|
||||
break;
|
||||
|
||||
case CDK_CTL_COMPRESS:
|
||||
if (set)
|
||||
{
|
||||
int algo = va_arg (arg_ptr, int);
|
||||
int level = va_arg (arg_ptr, int);
|
||||
handle_set_compress (hd, algo, level);
|
||||
}
|
||||
else
|
||||
val = hd->compress.algo;
|
||||
break;
|
||||
|
||||
case CDK_CTL_S2K:
|
||||
if (set)
|
||||
{
|
||||
int mode = va_arg (arg_ptr, int);
|
||||
int digest = va_arg (arg_ptr, int);
|
||||
int cipher = va_arg (arg_ptr, int);
|
||||
handle_set_s2k (hd, mode, digest, cipher);
|
||||
}
|
||||
else
|
||||
val = hd->_s2k.mode;
|
||||
break;
|
||||
|
||||
case CDK_CTL_FORCE_DIGEST:
|
||||
if (set)
|
||||
hd->opt.force_digest = va_arg (arg_ptr, int);
|
||||
else
|
||||
val = hd->opt.force_digest;
|
||||
break;
|
||||
|
||||
case CDK_CTL_BLOCKMODE_ON:
|
||||
if (set)
|
||||
hd->opt.blockmode = va_arg (arg_ptr, int);
|
||||
else
|
||||
val = hd->opt.blockmode;
|
||||
break;
|
||||
|
||||
default:
|
||||
val = -1;
|
||||
break;
|
||||
}
|
||||
va_end (arg_ptr);
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* cdk_handle_new:
|
||||
* @r_ctx: context to store the handle
|
||||
*
|
||||
* create a new session handle.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_handle_new (cdk_ctx_t * r_ctx)
|
||||
{
|
||||
cdk_ctx_t c;
|
||||
|
||||
if (!r_ctx)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
c = cdk_calloc (1, sizeof *c);
|
||||
if (!c)
|
||||
return CDK_Out_Of_Core;
|
||||
|
||||
/* For S2K use the iterated and salted mode and use the
|
||||
default digest and cipher algorithms. Because the MDC
|
||||
feature will be used, the default cipher should use a
|
||||
blocksize of 128 bits. */
|
||||
c->_s2k.mode = CDK_S2K_ITERSALTED;
|
||||
c->_s2k.digest_algo = DEFAULT_DIGEST_ALGO;
|
||||
|
||||
c->opt.mdc = 1;
|
||||
c->opt.compress = 1;
|
||||
c->opt.armor = 0;
|
||||
c->opt.textmode = 0;
|
||||
|
||||
c->digest_algo = DEFAULT_DIGEST_ALGO;
|
||||
c->cipher_algo = DEFAULT_CIPHER_ALGO;
|
||||
|
||||
c->compress.algo = CDK_COMPRESS_ZIP;
|
||||
c->compress.level = 6;
|
||||
|
||||
*r_ctx = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_handle_set_keyring:
|
||||
* @hd: session handle
|
||||
* @type: public=0 or secret=1 keyring type
|
||||
* @kringname: file name of the keyring which shall be used.
|
||||
*
|
||||
* Convenient function to set the keyring for the current session.
|
||||
*/
|
||||
cdk_error_t
|
||||
cdk_handle_set_keyring (cdk_ctx_t hd, int type, const char *kringname)
|
||||
{
|
||||
cdk_keydb_hd_t db;
|
||||
cdk_error_t err;
|
||||
|
||||
err = cdk_keydb_new_from_file (&db, type, kringname);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!type)
|
||||
hd->db.pub = db;
|
||||
else
|
||||
hd->db.sec = db;
|
||||
hd->db.close_db = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_handle_set_keydb:
|
||||
* @hd: session handle
|
||||
* @db: the database handle
|
||||
*
|
||||
* set the key database handle.
|
||||
* the function automatically detects whether this is a public or
|
||||
* secret keyring and the right handle is set.
|
||||
**/
|
||||
void
|
||||
cdk_handle_set_keydb (cdk_ctx_t hd, cdk_keydb_hd_t db)
|
||||
{
|
||||
if (!hd)
|
||||
return;
|
||||
if (_cdk_keydb_is_secret (db))
|
||||
hd->db.sec = db;
|
||||
else
|
||||
hd->db.pub = db;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_handle_get_keydb:
|
||||
* @hd: session handle
|
||||
* @type: type of the keyring
|
||||
*
|
||||
* Return the keydb handle from the session handle.
|
||||
* The caller should not free these handles.
|
||||
**/
|
||||
cdk_keydb_hd_t
|
||||
cdk_handle_get_keydb (cdk_ctx_t hd, int type)
|
||||
{
|
||||
if (!hd)
|
||||
return NULL;
|
||||
if (type == CDK_DBTYPE_PK_KEYRING)
|
||||
return hd->db.pub;
|
||||
else if (type == CDK_DBTYPE_SK_KEYRING)
|
||||
return hd->db.sec;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_handle_set_passphrase_cb:
|
||||
* @hd: session handle
|
||||
* @cb: callback function
|
||||
* @cb_value: the opaque value for the cb function
|
||||
*
|
||||
* set the passphrase callback.
|
||||
**/
|
||||
void
|
||||
cdk_handle_set_passphrase_cb (cdk_ctx_t hd,
|
||||
char *(*cb) (void *opa, const char *prompt),
|
||||
void *cb_value)
|
||||
{
|
||||
if (!hd)
|
||||
return;
|
||||
hd->passphrase_cb = cb;
|
||||
hd->passphrase_cb_value = cb_value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_handle_verify_get_result:
|
||||
* @hd: the session handle
|
||||
*
|
||||
* Return the verify result for the current session.
|
||||
* Do not free the pointer.
|
||||
**/
|
||||
cdk_verify_result_t
|
||||
cdk_handle_verify_get_result (cdk_ctx_t hd)
|
||||
{
|
||||
return hd->result.verify;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_handle_free:
|
||||
* @hd: the handle
|
||||
*
|
||||
* Release the main handle.
|
||||
**/
|
||||
void
|
||||
cdk_handle_free (cdk_ctx_t hd)
|
||||
{
|
||||
if (!hd)
|
||||
return;
|
||||
_cdk_result_verify_free (hd->result.verify);
|
||||
|
||||
/* If cdk_handle_set_keyring() were used, we need to free the key db
|
||||
handles here because the handles are not controlled by the user. */
|
||||
if (hd->db.close_db)
|
||||
{
|
||||
if (hd->db.pub)
|
||||
cdk_keydb_free (hd->db.pub);
|
||||
if (hd->db.sec)
|
||||
cdk_keydb_free (hd->db.sec);
|
||||
hd->db.pub = hd->db.sec = NULL;
|
||||
}
|
||||
cdk_free (hd->dek);
|
||||
cdk_free (hd);
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
/* main.h
|
||||
* Copyright (C) 2002, 2003, 2007 Timo Schulz
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifndef CDK_MAIN_H
|
||||
#define CDK_MAIN_H
|
||||
|
||||
#include <gcrypt.h>
|
||||
#include "types.h"
|
||||
|
||||
/* The general size of a buffer for the variou modules. */
|
||||
#define BUFSIZE 8192
|
||||
|
||||
/* This is the default block size for the partial length packet mode. */
|
||||
#define DEF_BLOCKSIZE 8192
|
||||
#define DEF_BLOCKBITS 13 /* 2^13 = 8192 */
|
||||
|
||||
/* For now SHA-1 is used to create fingerprint for keys.
|
||||
But if this will ever change, it is a good idea to
|
||||
have a constant for it to avoid to change it in all files. */
|
||||
#define KEY_FPR_LEN 20
|
||||
|
||||
#include "context.h"
|
||||
|
||||
/* The maximal amount of bits a multi precsion integer can have. */
|
||||
#define MAX_MPI_BITS 16384
|
||||
#define MAX_MPI_BYTES (MAX_MPI_BITS/8)
|
||||
|
||||
|
||||
/* Because newer DSA variants are not limited to SHA-1, we must consider
|
||||
that SHA-512 is used and increase the buffer size of the digest. */
|
||||
#define MAX_DIGEST_LEN 64
|
||||
|
||||
/* Helper to find out if the signature were made over a user ID
|
||||
or if the signature revokes a previous user ID. */
|
||||
#define IS_UID_SIG(s) (((s)->sig_class & ~3) == 0x10)
|
||||
#define IS_UID_REV(s) ((s)->sig_class == 0x30)
|
||||
|
||||
#define DEBUG_PKT (_cdk_get_log_level () == (CDK_LOG_DEBUG+1))
|
||||
|
||||
/* Helper to find out if a key has the requested capability. */
|
||||
#define KEY_CAN_ENCRYPT(a) (_cdk_pk_algo_usage ((a)) & CDK_KEY_USG_ENCR)
|
||||
#define KEY_CAN_SIGN(a) (_cdk_pk_algo_usage ((a)) & CDK_KEY_USG_SIGN)
|
||||
#define KEY_CAN_AUTH(a) (_cdk_pk_algo_usage ((a)) & CDK_KEY_USG_AUTH)
|
||||
|
||||
/* Helper macro to make sure the buffer is overwritten. */
|
||||
#define wipemem(_ptr,_len) do { \
|
||||
volatile char *_vptr = (volatile char *)(_ptr); \
|
||||
size_t _vlen = (_len); \
|
||||
while (_vlen) \
|
||||
{ \
|
||||
*_vptr = 0; \
|
||||
_vptr++; \
|
||||
_vlen--; \
|
||||
} } while (0)
|
||||
|
||||
/*-- armor.c --*/
|
||||
const char * _cdk_armor_get_lineend (void);
|
||||
|
||||
/*-- main.c --*/
|
||||
int _cdk_get_log_level (void);
|
||||
void _cdk_log_info (const char * fmt, ...);
|
||||
void _cdk_log_debug (const char * fmt, ...);
|
||||
char * _cdk_passphrase_get (cdk_ctx_t hd, const char *prompt);
|
||||
|
||||
/*-- misc.c --*/
|
||||
int _cdk_check_args( int overwrite, const char * in, const char * out );
|
||||
u32 _cdk_buftou32 (const byte * buf);
|
||||
void _cdk_u32tobuf (u32 u, byte * buf);
|
||||
const char *_cdk_memistr (const char * buf, size_t buflen, const char * sub);
|
||||
cdk_error_t _cdk_map_gcry_error (gcry_error_t err);
|
||||
#define map_gcry_error(err) _cdk_map_gcry_error (err)
|
||||
|
||||
/* Helper to provide case insentensive strstr version. */
|
||||
#define stristr(haystack, needle) \
|
||||
_cdk_memistr((haystack), strlen (haystack), (needle))
|
||||
|
||||
/*-- proc-packet.c --*/
|
||||
cdk_error_t _cdk_proc_packets (cdk_ctx_t hd, cdk_stream_t inp,
|
||||
cdk_stream_t data,
|
||||
const char *output, cdk_stream_t outstream,
|
||||
gcry_md_hd_t md);
|
||||
cdk_error_t _cdk_pkt_write2 (cdk_stream_t out, int pkttype, void *pktctx);
|
||||
|
||||
/*-- pubkey.c --*/
|
||||
u32 _cdk_pkt_get_keyid (cdk_packet_t pkt, u32 * keyid);
|
||||
cdk_error_t _cdk_pkt_get_fingerprint (cdk_packet_t pkt, byte *fpr);
|
||||
int _cdk_pk_algo_usage (int algo);
|
||||
int _cdk_pk_test_algo (int algo, unsigned int usage);
|
||||
int _cdk_sk_get_csum (cdk_pkt_seckey_t sk);
|
||||
|
||||
/*-- new-packet.c --*/
|
||||
byte * _cdk_subpkt_get_array (cdk_subpkt_t s, int count, size_t * r_nbytes);
|
||||
cdk_error_t _cdk_subpkt_copy (cdk_subpkt_t * r_dst, cdk_subpkt_t src);
|
||||
void _cdk_pkt_detach_free (cdk_packet_t pkt, int *r_pkttype, void **ctx);
|
||||
|
||||
/*-- sig-check.c --*/
|
||||
cdk_error_t _cdk_sig_check (cdk_pkt_pubkey_t pk, cdk_pkt_signature_t sig,
|
||||
gcry_md_hd_t digest, int * r_expired);
|
||||
cdk_error_t _cdk_hash_sig_data (cdk_pkt_signature_t sig, gcry_md_hd_t hd);
|
||||
cdk_error_t _cdk_hash_userid (cdk_pkt_userid_t uid, int sig_version, gcry_md_hd_t md);
|
||||
cdk_error_t _cdk_hash_pubkey (cdk_pkt_pubkey_t pk, gcry_md_hd_t md,
|
||||
int use_fpr);
|
||||
cdk_error_t _cdk_pk_check_sig (cdk_keydb_hd_t hd,
|
||||
cdk_kbnode_t knode,
|
||||
cdk_kbnode_t snode, int *is_selfsig);
|
||||
|
||||
/*-- kbnode.c --*/
|
||||
void _cdk_kbnode_add (cdk_kbnode_t root, cdk_kbnode_t node);
|
||||
void _cdk_kbnode_clone (cdk_kbnode_t node);
|
||||
|
||||
/*-- sesskey.c --*/
|
||||
cdk_error_t _cdk_digest_encode_pkcs1 (byte **r_md, size_t *r_mdlen,
|
||||
int pk_algo,
|
||||
const byte * md,
|
||||
int digest_algo, unsigned nbits);
|
||||
cdk_error_t _cdk_sk_unprotect_auto (cdk_ctx_t hd, cdk_pkt_seckey_t sk);
|
||||
|
||||
/*-- keydb.c --*/
|
||||
int _cdk_keydb_is_secret (cdk_keydb_hd_t db);
|
||||
cdk_error_t _cdk_keydb_get_pk_byusage (cdk_keydb_hd_t hd, const char * name,
|
||||
cdk_pkt_pubkey_t * ret_pk, int usage);
|
||||
cdk_error_t _cdk_keydb_get_sk_byusage (cdk_keydb_hd_t hd, const char * name,
|
||||
cdk_pkt_seckey_t * ret_sk, int usage);
|
||||
cdk_error_t _cdk_keydb_check_userid (cdk_keydb_hd_t hd, u32 * keyid,
|
||||
const char * id);
|
||||
|
||||
/*-- sign.c --*/
|
||||
int _cdk_sig_hash_for (cdk_pkt_pubkey_t pk);
|
||||
void _cdk_trim_string (char * s, int canon);
|
||||
cdk_error_t _cdk_sig_create (cdk_pkt_pubkey_t pk, cdk_pkt_signature_t sig);
|
||||
cdk_error_t _cdk_sig_complete (cdk_pkt_signature_t sig, cdk_pkt_seckey_t sk,
|
||||
gcry_md_hd_t hd);
|
||||
|
||||
/*-- stream.c --*/
|
||||
void _cdk_stream_set_compress_algo (cdk_stream_t s, int algo);
|
||||
cdk_error_t _cdk_stream_open_mode (const char *file, const char *mode,
|
||||
cdk_stream_t *ret_s);
|
||||
void * _cdk_stream_get_opaque( cdk_stream_t s, int fid );
|
||||
const char * _cdk_stream_get_fname( cdk_stream_t s );
|
||||
FILE * _cdk_stream_get_fp( cdk_stream_t s );
|
||||
int _cdk_stream_gets( cdk_stream_t s, char * buf, size_t count );
|
||||
cdk_error_t _cdk_stream_append( const char * file, cdk_stream_t * ret_s );
|
||||
int _cdk_stream_get_errno( cdk_stream_t s );
|
||||
cdk_error_t _cdk_stream_set_blockmode( cdk_stream_t s, size_t nbytes );
|
||||
int _cdk_stream_get_blockmode( cdk_stream_t s );
|
||||
int _cdk_stream_puts( cdk_stream_t s, const char * buf );
|
||||
cdk_error_t _cdk_stream_fpopen (FILE * fp, unsigned write_mode,
|
||||
cdk_stream_t *ret_out);
|
||||
|
||||
/*-- verify.c --*/
|
||||
void _cdk_result_verify_free (cdk_verify_result_t res);
|
||||
cdk_verify_result_t _cdk_result_verify_new (void);
|
||||
|
||||
|
||||
/*-- read-packet.c --*/
|
||||
size_t _cdk_pkt_read_len (FILE * inp, size_t *ret_partial);
|
||||
|
||||
/*-- write-packet.c --*/
|
||||
cdk_error_t _cdk_pkt_write_fp( FILE * out, cdk_packet_t pkt );
|
||||
|
||||
/*-- seskey.c --*/
|
||||
cdk_error_t _cdk_s2k_copy (cdk_s2k_t *r_dst, cdk_s2k_t src);
|
||||
|
||||
cdk_error_t cdk_dek_encode_pkcs1 (cdk_dek_t dek, size_t nbits,
|
||||
gcry_mpi_t *r_enc);
|
||||
cdk_error_t cdk_dek_decode_pkcs1 (cdk_dek_t * ret_dek, gcry_mpi_t esk);
|
||||
cdk_error_t cdk_dek_extract (cdk_dek_t * ret_dek, cdk_ctx_t hd,
|
||||
cdk_pkt_pubkey_enc_t enc,
|
||||
cdk_pkt_seckey_t sk );
|
||||
|
||||
#endif /* CDK_MAIN_H */
|
||||
@@ -1,571 +0,0 @@
|
||||
/* misc.c
|
||||
* Copyright (C) 2002, 2003 Timo Schulz
|
||||
* Copyright (C) 1998-2002, 2007 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "opencdk.h"
|
||||
#include "main.h"
|
||||
|
||||
|
||||
u32
|
||||
_cdk_buftou32 (const byte * buf)
|
||||
{
|
||||
u32 u;
|
||||
|
||||
if (!buf)
|
||||
return 0;
|
||||
u = buf[0] << 24;
|
||||
u |= buf[1] << 16;
|
||||
u |= buf[2] << 8;
|
||||
u |= buf[3];
|
||||
return u;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_cdk_u32tobuf (u32 u, byte * buf)
|
||||
{
|
||||
if (!buf)
|
||||
return;
|
||||
buf[0] = u >> 24;
|
||||
buf[1] = u >> 16;
|
||||
buf[2] = u >> 8;
|
||||
buf[3] = u;
|
||||
}
|
||||
|
||||
|
||||
static const char *
|
||||
parse_version_number (const char *s, int *number)
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
if (*s == '0' && isdigit (s[1]))
|
||||
return NULL;
|
||||
/* leading zeros are not allowed */
|
||||
for (; isdigit (*s); s++)
|
||||
{
|
||||
val *= 10;
|
||||
val += *s - '0';
|
||||
}
|
||||
*number = val;
|
||||
return val < 0 ? NULL : s;
|
||||
}
|
||||
|
||||
|
||||
static const char *
|
||||
parse_version_string (const char *s, int *major, int *minor, int *micro)
|
||||
{
|
||||
s = parse_version_number (s, major);
|
||||
if (!s || *s != '.')
|
||||
return NULL;
|
||||
s++;
|
||||
s = parse_version_number (s, minor);
|
||||
if (!s || *s != '.')
|
||||
return NULL;
|
||||
s++;
|
||||
s = parse_version_number (s, micro);
|
||||
if (!s)
|
||||
return NULL;
|
||||
return s; /* patchlevel */
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_check_version:
|
||||
* @req_version: The requested version
|
||||
*
|
||||
* Check that the the version of the library is at minimum the requested
|
||||
* one and return the version string; return NULL if the condition is
|
||||
* not satisfied. If a NULL is passed to this function, no check is done,
|
||||
*but the version string is simply returned.
|
||||
**/
|
||||
const char *
|
||||
cdk_check_version (const char *req_version)
|
||||
{
|
||||
const char *ver = VERSION;
|
||||
int my_major, my_minor, my_micro;
|
||||
int rq_major, rq_minor, rq_micro;
|
||||
const char *my_plvl, *rq_plvl;
|
||||
|
||||
if (!req_version)
|
||||
return ver;
|
||||
my_plvl = parse_version_string (ver, &my_major, &my_minor, &my_micro);
|
||||
if (!my_plvl)
|
||||
return NULL;
|
||||
/* very strange our own version is bogus */
|
||||
rq_plvl = parse_version_string (req_version, &rq_major, &rq_minor,
|
||||
&rq_micro);
|
||||
if (!rq_plvl)
|
||||
return NULL; /* req version string is invalid */
|
||||
if (my_major > rq_major
|
||||
|| (my_major == rq_major && my_minor > rq_minor)
|
||||
|| (my_major == rq_major && my_minor == rq_minor
|
||||
&& my_micro > rq_micro)
|
||||
|| (my_major == rq_major && my_minor == rq_minor
|
||||
&& my_micro == rq_micro && strcmp (my_plvl, rq_plvl) >= 0))
|
||||
return ver;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_strlist_free:
|
||||
* @sl: the string list
|
||||
*
|
||||
* Release the string list object.
|
||||
**/
|
||||
void
|
||||
cdk_strlist_free (cdk_strlist_t sl)
|
||||
{
|
||||
cdk_strlist_t sl2;
|
||||
|
||||
for (; sl; sl = sl2)
|
||||
{
|
||||
sl2 = sl->next;
|
||||
cdk_free (sl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_strlist_add:
|
||||
* @list: destination string list
|
||||
* @string: the string to add
|
||||
*
|
||||
* Add the given list to the string list.
|
||||
**/
|
||||
cdk_strlist_t
|
||||
cdk_strlist_add (cdk_strlist_t * list, const char *string)
|
||||
{
|
||||
cdk_strlist_t sl;
|
||||
|
||||
if (!string)
|
||||
return NULL;
|
||||
|
||||
sl = cdk_calloc (1, sizeof *sl + strlen (string) + 1);
|
||||
if (!sl)
|
||||
return NULL;
|
||||
strcpy (sl->d, string);
|
||||
sl->next = *list;
|
||||
*list = sl;
|
||||
return sl;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_strlist_next:
|
||||
* @root: the opaque string list.
|
||||
* @r_str: optional argument to store the string data.
|
||||
*
|
||||
* Return the next string list node from @root. The optional
|
||||
* argument @r_str return the data of the current (!) node.
|
||||
**/
|
||||
cdk_strlist_t
|
||||
cdk_strlist_next (cdk_strlist_t root, const char **r_str)
|
||||
{
|
||||
cdk_strlist_t node;
|
||||
|
||||
if (root && r_str)
|
||||
*r_str = root->d;
|
||||
for (node = root->next; node; node = node->next)
|
||||
return node;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
_cdk_memistr (const char *buf, size_t buflen, const char *sub)
|
||||
{
|
||||
const byte *t, *s;
|
||||
size_t n;
|
||||
|
||||
for (t = (byte *) buf, n = buflen, s = (byte *) sub; n; t++, n--)
|
||||
{
|
||||
if (toupper (*t) == toupper (*s))
|
||||
{
|
||||
for (buf = t++, buflen = n--, s++;
|
||||
n && toupper (*t) == toupper ((byte) * s); t++, s++, n--)
|
||||
;
|
||||
if (!*s)
|
||||
return buf;
|
||||
t = (byte *) buf;
|
||||
n = buflen;
|
||||
s = (byte *) sub;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_utf8_encode:
|
||||
* @string:
|
||||
*
|
||||
* Encode the given string in utf8 and return it.
|
||||
**/
|
||||
char *
|
||||
cdk_utf8_encode (const char *string)
|
||||
{
|
||||
const byte *s;
|
||||
char *buffer;
|
||||
byte *p;
|
||||
size_t length;
|
||||
|
||||
/* FIXME: We should use iconv if possible for utf8 issues. */
|
||||
for (s = (const byte *) string, length = 0; *s; s++)
|
||||
{
|
||||
length++;
|
||||
if (*s & 0x80)
|
||||
length++;
|
||||
}
|
||||
|
||||
buffer = cdk_calloc (1, length + 1);
|
||||
for (p = (byte *) buffer, s = (byte *) string; *s; s++)
|
||||
{
|
||||
if (*s & 0x80)
|
||||
{
|
||||
*p++ = 0xc0 | ((*s >> 6) & 3);
|
||||
*p++ = 0x80 | (*s & 0x3f);
|
||||
}
|
||||
else
|
||||
*p++ = *s;
|
||||
}
|
||||
*p = 0;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_utf8_decode:
|
||||
* @string: the string to decode
|
||||
* @length: the length of the string
|
||||
* @delim: the delimiter
|
||||
*
|
||||
* Decode the given utf8 string and return the native representation.
|
||||
**/
|
||||
char *
|
||||
cdk_utf8_decode (const char *string, size_t length, int delim)
|
||||
{
|
||||
int nleft;
|
||||
int i;
|
||||
byte encbuf[8];
|
||||
int encidx;
|
||||
const byte *s;
|
||||
size_t n;
|
||||
byte *buffer = NULL, *p = NULL;
|
||||
unsigned long val = 0;
|
||||
size_t slen;
|
||||
int resync = 0;
|
||||
|
||||
/* 1. pass (p==NULL): count the extended utf-8 characters */
|
||||
/* 2. pass (p!=NULL): create string */
|
||||
for (;;)
|
||||
{
|
||||
for (slen = length, nleft = encidx = 0, n = 0, s = (byte *) string;
|
||||
slen; s++, slen--)
|
||||
{
|
||||
if (resync)
|
||||
{
|
||||
if (!(*s < 128 || (*s >= 0xc0 && *s <= 0xfd)))
|
||||
{
|
||||
/* still invalid */
|
||||
if (p)
|
||||
{
|
||||
sprintf ((char *) p, "\\x%02x", *s);
|
||||
p += 4;
|
||||
}
|
||||
n += 4;
|
||||
continue;
|
||||
}
|
||||
resync = 0;
|
||||
}
|
||||
if (!nleft)
|
||||
{
|
||||
if (!(*s & 0x80))
|
||||
{ /* plain ascii */
|
||||
if (*s < 0x20 || *s == 0x7f || *s == delim ||
|
||||
(delim && *s == '\\'))
|
||||
{
|
||||
n++;
|
||||
if (p)
|
||||
*p++ = '\\';
|
||||
switch (*s)
|
||||
{
|
||||
case '\n':
|
||||
n++;
|
||||
if (p)
|
||||
*p++ = 'n';
|
||||
break;
|
||||
case '\r':
|
||||
n++;
|
||||
if (p)
|
||||
*p++ = 'r';
|
||||
break;
|
||||
case '\f':
|
||||
n++;
|
||||
if (p)
|
||||
*p++ = 'f';
|
||||
break;
|
||||
case '\v':
|
||||
n++;
|
||||
if (p)
|
||||
*p++ = 'v';
|
||||
break;
|
||||
case '\b':
|
||||
n++;
|
||||
if (p)
|
||||
*p++ = 'b';
|
||||
break;
|
||||
case 0:
|
||||
n++;
|
||||
if (p)
|
||||
*p++ = '0';
|
||||
break;
|
||||
default:
|
||||
n += 3;
|
||||
if (p)
|
||||
{
|
||||
sprintf ((char *) p, "x%02x", *s);
|
||||
p += 3;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p)
|
||||
*p++ = *s;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
else if ((*s & 0xe0) == 0xc0)
|
||||
{ /* 110x xxxx */
|
||||
val = *s & 0x1f;
|
||||
nleft = 1;
|
||||
encidx = 0;
|
||||
encbuf[encidx++] = *s;
|
||||
}
|
||||
else if ((*s & 0xf0) == 0xe0)
|
||||
{ /* 1110 xxxx */
|
||||
val = *s & 0x0f;
|
||||
nleft = 2;
|
||||
encidx = 0;
|
||||
encbuf[encidx++] = *s;
|
||||
}
|
||||
else if ((*s & 0xf8) == 0xf0)
|
||||
{ /* 1111 0xxx */
|
||||
val = *s & 0x07;
|
||||
nleft = 3;
|
||||
encidx = 0;
|
||||
encbuf[encidx++] = *s;
|
||||
}
|
||||
else if ((*s & 0xfc) == 0xf8)
|
||||
{ /* 1111 10xx */
|
||||
val = *s & 0x03;
|
||||
nleft = 4;
|
||||
encidx = 0;
|
||||
encbuf[encidx++] = *s;
|
||||
}
|
||||
else if ((*s & 0xfe) == 0xfc)
|
||||
{ /* 1111 110x */
|
||||
val = *s & 0x01;
|
||||
nleft = 5;
|
||||
encidx = 0;
|
||||
encbuf[encidx++] = *s;
|
||||
}
|
||||
else
|
||||
{ /* invalid encoding: print as \xnn */
|
||||
if (p)
|
||||
{
|
||||
sprintf ((char *) p, "\\x%02x", *s);
|
||||
p += 4;
|
||||
}
|
||||
n += 4;
|
||||
resync = 1;
|
||||
}
|
||||
}
|
||||
else if (*s < 0x80 || *s >= 0xc0)
|
||||
{ /* invalid */
|
||||
if (p)
|
||||
{
|
||||
for (i = 0; i < encidx; i++)
|
||||
{
|
||||
sprintf ((char *) p, "\\x%02x", encbuf[i]);
|
||||
p += 4;
|
||||
}
|
||||
sprintf ((char *) p, "\\x%02x", *s);
|
||||
p += 4;
|
||||
}
|
||||
n += 4 + 4 * encidx;
|
||||
nleft = 0;
|
||||
encidx = 0;
|
||||
resync = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
encbuf[encidx++] = *s;
|
||||
val <<= 6;
|
||||
val |= *s & 0x3f;
|
||||
if (!--nleft)
|
||||
{ /* ready native set */
|
||||
if (val >= 0x80 && val < 256)
|
||||
{
|
||||
n++; /* we can simply print this character */
|
||||
if (p)
|
||||
*p++ = val;
|
||||
}
|
||||
else
|
||||
{ /* we do not have a translation: print utf8 */
|
||||
if (p)
|
||||
{
|
||||
for (i = 0; i < encidx; i++)
|
||||
{
|
||||
sprintf ((char *) p, "\\x%02x", encbuf[i]);
|
||||
p += 4;
|
||||
}
|
||||
}
|
||||
n += encidx * 4;
|
||||
encidx = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (!buffer) /* allocate the buffer after the first pass */
|
||||
buffer = p = cdk_malloc (n + 1);
|
||||
else
|
||||
{
|
||||
*p = 0; /* make a string */
|
||||
return (char *) buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Map the gcrypt error to a valid opencdk error constant. */
|
||||
cdk_error_t
|
||||
_cdk_map_gcry_error (gcry_error_t err)
|
||||
{
|
||||
/* FIXME: We need to catch them all. */
|
||||
switch (gpg_err_code (err))
|
||||
{
|
||||
case GPG_ERR_NO_ERROR:
|
||||
return CDK_Success;
|
||||
case GPG_ERR_INV_VALUE:
|
||||
return CDK_Inv_Value;
|
||||
case GPG_ERR_GENERAL:
|
||||
return CDK_General_Error;
|
||||
case GPG_ERR_INV_PACKET:
|
||||
return CDK_Inv_Packet;
|
||||
case GPG_ERR_TOO_SHORT:
|
||||
return CDK_Too_Short;
|
||||
case GPG_ERR_TOO_LARGE:
|
||||
return CDK_Inv_Value;
|
||||
case GPG_ERR_NO_PUBKEY:
|
||||
case GPG_ERR_NO_SECKEY:
|
||||
return CDK_Error_No_Key;
|
||||
case GPG_ERR_BAD_SIGNATURE:
|
||||
return CDK_Bad_Sig;
|
||||
case GPG_ERR_NO_DATA:
|
||||
return CDK_No_Data;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return (cdk_error_t) err;
|
||||
}
|
||||
|
||||
|
||||
/* Remove all trailing white spaces from the string. */
|
||||
void
|
||||
_cdk_trim_string (char *s, int canon)
|
||||
{
|
||||
while (s && *s &&
|
||||
(s[strlen (s) - 1] == '\t' ||
|
||||
s[strlen (s) - 1] == '\r' ||
|
||||
s[strlen (s) - 1] == '\n' || s[strlen (s) - 1] == ' '))
|
||||
s[strlen (s) - 1] = '\0';
|
||||
if (canon)
|
||||
strcat (s, "\r\n");
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
_cdk_check_args (int overwrite, const char *in, const char *out)
|
||||
{
|
||||
struct stat stbuf;
|
||||
|
||||
if (!in || !out)
|
||||
return CDK_Inv_Value;
|
||||
if (strlen (in) == strlen (out) && strcmp (in, out) == 0)
|
||||
return CDK_Inv_Mode;
|
||||
if (!overwrite && !stat (out, &stbuf))
|
||||
return CDK_Inv_Mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
FILE *
|
||||
my_tmpfile (void)
|
||||
{
|
||||
/* Because the tmpfile() version of wine is not really useful,
|
||||
we implement our own version to avoid problems with 'make check'. */
|
||||
static const char *letters = "abcdefghijklmnopqrstuvwxyz";
|
||||
char buf[512], rnd[24];
|
||||
FILE *fp;
|
||||
int fd, i;
|
||||
|
||||
gcry_create_nonce (rnd, DIM (rnd));
|
||||
for (i = 0; i < DIM (rnd) - 1; i++)
|
||||
{
|
||||
char c = letters[(unsigned char) rnd[i] % 26];
|
||||
rnd[i] = c;
|
||||
}
|
||||
rnd[DIM (rnd) - 1] = 0;
|
||||
if (!GetTempPath (464, buf))
|
||||
return NULL;
|
||||
strcat (buf, "_cdk_");
|
||||
strcat (buf, rnd);
|
||||
|
||||
/* We need to make sure the file will be deleted when it is closed. */
|
||||
fd = _open (buf, _O_CREAT | _O_EXCL | _O_TEMPORARY |
|
||||
_O_RDWR | _O_BINARY, _S_IREAD | _S_IWRITE);
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
fp = fdopen (fd, "w+b");
|
||||
if (fp != NULL)
|
||||
return fp;
|
||||
_close (fd);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
FILE *
|
||||
my_tmpfile (void)
|
||||
{
|
||||
return tmpfile ();
|
||||
}
|
||||
#endif
|
||||
@@ -1,874 +0,0 @@
|
||||
/* new-packet.c - packet handling (freeing, copying, ...)
|
||||
* Copyright (C) 2001, 2002, 2003, 2007 Timo Schulz
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "opencdk.h"
|
||||
#include "main.h"
|
||||
#include "packet.h"
|
||||
|
||||
|
||||
/* Release an array of MPI values. */
|
||||
void
|
||||
_cdk_free_mpibuf (size_t n, gcry_mpi_t * array)
|
||||
{
|
||||
while (n--)
|
||||
{
|
||||
gcry_mpi_release (array[n]);
|
||||
array[n] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pkt_new:
|
||||
* @r_pkt: the new packet
|
||||
*
|
||||
* Allocate a new packet.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_pkt_new (cdk_packet_t * r_pkt)
|
||||
{
|
||||
cdk_packet_t pkt;
|
||||
|
||||
if (!r_pkt)
|
||||
return CDK_Inv_Value;
|
||||
pkt = cdk_calloc (1, sizeof *pkt);
|
||||
if (!pkt)
|
||||
return CDK_Out_Of_Core;
|
||||
*r_pkt = pkt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
free_symkey_enc (cdk_pkt_symkey_enc_t enc)
|
||||
{
|
||||
if (!enc)
|
||||
return;
|
||||
cdk_s2k_free (enc->s2k);
|
||||
cdk_free (enc);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
free_pubkey_enc (cdk_pkt_pubkey_enc_t enc)
|
||||
{
|
||||
size_t nenc;
|
||||
|
||||
if (!enc)
|
||||
return;
|
||||
|
||||
nenc = cdk_pk_get_nenc (enc->pubkey_algo);
|
||||
_cdk_free_mpibuf (nenc, enc->mpi);
|
||||
cdk_free (enc);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
free_literal (cdk_pkt_literal_t pt)
|
||||
{
|
||||
if (!pt)
|
||||
return;
|
||||
/* The buffer which is referenced in this packet is closed
|
||||
elsewhere. To close it here would cause a double close. */
|
||||
cdk_free (pt);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_cdk_free_userid (cdk_pkt_userid_t uid)
|
||||
{
|
||||
if (!uid)
|
||||
return;
|
||||
|
||||
cdk_free (uid->prefs);
|
||||
uid->prefs = NULL;
|
||||
cdk_free (uid->attrib_img);
|
||||
uid->attrib_img = NULL;
|
||||
cdk_free (uid);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_cdk_free_signature (cdk_pkt_signature_t sig)
|
||||
{
|
||||
cdk_desig_revoker_t r;
|
||||
size_t nsig;
|
||||
|
||||
if (!sig)
|
||||
return;
|
||||
|
||||
nsig = cdk_pk_get_nsig (sig->pubkey_algo);
|
||||
_cdk_free_mpibuf (nsig, sig->mpi);
|
||||
|
||||
cdk_subpkt_free (sig->hashed);
|
||||
sig->hashed = NULL;
|
||||
cdk_subpkt_free (sig->unhashed);
|
||||
sig->unhashed = NULL;
|
||||
while (sig->revkeys)
|
||||
{
|
||||
r = sig->revkeys->next;
|
||||
cdk_free (sig->revkeys);
|
||||
sig->revkeys = r;
|
||||
}
|
||||
cdk_free (sig);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
cdk_pk_release (cdk_pubkey_t pk)
|
||||
{
|
||||
size_t npkey;
|
||||
|
||||
if (!pk)
|
||||
return;
|
||||
|
||||
npkey = cdk_pk_get_npkey (pk->pubkey_algo);
|
||||
_cdk_free_userid (pk->uid);
|
||||
pk->uid = NULL;
|
||||
cdk_free (pk->prefs);
|
||||
pk->prefs = NULL;
|
||||
_cdk_free_mpibuf (npkey, pk->mpi);
|
||||
cdk_free (pk);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
cdk_sk_release (cdk_seckey_t sk)
|
||||
{
|
||||
size_t nskey;
|
||||
|
||||
if (!sk)
|
||||
return;
|
||||
|
||||
nskey = cdk_pk_get_nskey (sk->pubkey_algo);
|
||||
_cdk_free_mpibuf (nskey, sk->mpi);
|
||||
cdk_free (sk->encdata);
|
||||
sk->encdata = NULL;
|
||||
cdk_pk_release (sk->pk);
|
||||
sk->pk = NULL;
|
||||
cdk_s2k_free (sk->protect.s2k);
|
||||
sk->protect.s2k = NULL;
|
||||
cdk_free (sk);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
free_encrypted (cdk_pkt_encrypted_t enc)
|
||||
{
|
||||
if (!enc)
|
||||
return;
|
||||
|
||||
/* This is just a reference for the filters to know where
|
||||
the encrypted data starts and to read from the sream. It
|
||||
us closed elsewhere and to close it here would double close it. */
|
||||
/*cdk_stream_close (enc->buf); */
|
||||
enc->buf = NULL;
|
||||
cdk_free (enc);
|
||||
}
|
||||
|
||||
|
||||
/* Detach the openpgp packet from the packet structure
|
||||
and release the packet structure itself. */
|
||||
void
|
||||
_cdk_pkt_detach_free (cdk_packet_t pkt, int *r_pkttype, void **ctx)
|
||||
{
|
||||
/* For now we just allow this for keys. */
|
||||
switch (pkt->pkttype)
|
||||
{
|
||||
case CDK_PKT_PUBLIC_KEY:
|
||||
case CDK_PKT_PUBLIC_SUBKEY:
|
||||
*ctx = pkt->pkt.public_key;
|
||||
break;
|
||||
|
||||
case CDK_PKT_SECRET_KEY:
|
||||
case CDK_PKT_SECRET_SUBKEY:
|
||||
*ctx = pkt->pkt.secret_key;
|
||||
break;
|
||||
|
||||
default:
|
||||
*r_pkttype = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* The caller might expect a specific packet type and
|
||||
is not interested to store it for later use. */
|
||||
if (r_pkttype)
|
||||
*r_pkttype = pkt->pkttype;
|
||||
|
||||
cdk_free (pkt);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
cdk_pkt_free (cdk_packet_t pkt)
|
||||
{
|
||||
if (!pkt)
|
||||
return;
|
||||
|
||||
switch (pkt->pkttype)
|
||||
{
|
||||
case CDK_PKT_ATTRIBUTE:
|
||||
case CDK_PKT_USER_ID:
|
||||
_cdk_free_userid (pkt->pkt.user_id);
|
||||
break;
|
||||
case CDK_PKT_PUBLIC_KEY:
|
||||
case CDK_PKT_PUBLIC_SUBKEY:
|
||||
cdk_pk_release (pkt->pkt.public_key);
|
||||
break;
|
||||
case CDK_PKT_SECRET_KEY:
|
||||
case CDK_PKT_SECRET_SUBKEY:
|
||||
cdk_sk_release (pkt->pkt.secret_key);
|
||||
break;
|
||||
case CDK_PKT_SIGNATURE:
|
||||
_cdk_free_signature (pkt->pkt.signature);
|
||||
break;
|
||||
case CDK_PKT_PUBKEY_ENC:
|
||||
free_pubkey_enc (pkt->pkt.pubkey_enc);
|
||||
break;
|
||||
case CDK_PKT_SYMKEY_ENC:
|
||||
free_symkey_enc (pkt->pkt.symkey_enc);
|
||||
break;
|
||||
case CDK_PKT_MDC:
|
||||
cdk_free (pkt->pkt.mdc);
|
||||
break;
|
||||
case CDK_PKT_ENCRYPTED:
|
||||
case CDK_PKT_ENCRYPTED_MDC:
|
||||
free_encrypted (pkt->pkt.encrypted);
|
||||
break;
|
||||
case CDK_PKT_ONEPASS_SIG:
|
||||
cdk_free (pkt->pkt.onepass_sig);
|
||||
break;
|
||||
case CDK_PKT_LITERAL:
|
||||
free_literal (pkt->pkt.literal);
|
||||
break;
|
||||
case CDK_PKT_COMPRESSED:
|
||||
cdk_free (pkt->pkt.compressed);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Reset the packet type to avoid, when cdk_pkt_release() will be
|
||||
used, that the second cdk_pkt_free() call will double free the data. */
|
||||
pkt->pkttype = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pkt_release:
|
||||
* @pkt: the packet
|
||||
*
|
||||
* Free the contents of the given package and
|
||||
* release the memory of the structure.
|
||||
**/
|
||||
void
|
||||
cdk_pkt_release (cdk_packet_t pkt)
|
||||
{
|
||||
if (!pkt)
|
||||
return;
|
||||
cdk_pkt_free (pkt);
|
||||
cdk_free (pkt);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pkt_alloc:
|
||||
* @r_pkt: output is the new packet
|
||||
* @pkttype: the requested packet type
|
||||
*
|
||||
* Allocate a new packet structure with the given packet type.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_pkt_alloc (cdk_packet_t * r_pkt, int pkttype)
|
||||
{
|
||||
cdk_packet_t pkt;
|
||||
int rc;
|
||||
|
||||
if (!r_pkt)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
rc = cdk_pkt_new (&pkt);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
switch (pkttype)
|
||||
{
|
||||
case CDK_PKT_USER_ID:
|
||||
pkt->pkt.user_id = cdk_calloc (1, sizeof pkt->pkt.user_id);
|
||||
if (!pkt->pkt.user_id)
|
||||
return CDK_Out_Of_Core;
|
||||
break;
|
||||
|
||||
case CDK_PKT_PUBLIC_KEY:
|
||||
case CDK_PKT_PUBLIC_SUBKEY:
|
||||
pkt->pkt.public_key = cdk_calloc (1, sizeof *pkt->pkt.public_key);
|
||||
if (!pkt->pkt.public_key)
|
||||
return CDK_Out_Of_Core;
|
||||
break;
|
||||
|
||||
case CDK_PKT_SECRET_KEY:
|
||||
case CDK_PKT_SECRET_SUBKEY:
|
||||
pkt->pkt.secret_key = cdk_calloc (1, sizeof *pkt->pkt.secret_key);
|
||||
pkt->pkt.secret_key->pk =
|
||||
cdk_calloc (1, sizeof *pkt->pkt.secret_key->pk);
|
||||
if (!pkt->pkt.secret_key || !pkt->pkt.secret_key->pk)
|
||||
return CDK_Out_Of_Core;
|
||||
break;
|
||||
|
||||
case CDK_PKT_SIGNATURE:
|
||||
pkt->pkt.signature = cdk_calloc (1, sizeof *pkt->pkt.signature);
|
||||
if (!pkt->pkt.signature)
|
||||
return CDK_Out_Of_Core;
|
||||
break;
|
||||
|
||||
case CDK_PKT_SYMKEY_ENC:
|
||||
pkt->pkt.symkey_enc = cdk_calloc (1, sizeof *pkt->pkt.symkey_enc);
|
||||
if (!pkt->pkt.symkey_enc)
|
||||
return CDK_Out_Of_Core;
|
||||
break;
|
||||
|
||||
case CDK_PKT_PUBKEY_ENC:
|
||||
pkt->pkt.pubkey_enc = cdk_calloc (1, sizeof *pkt->pkt.pubkey_enc);
|
||||
if (!pkt->pkt.pubkey_enc)
|
||||
return CDK_Out_Of_Core;
|
||||
break;
|
||||
|
||||
case CDK_PKT_MDC:
|
||||
pkt->pkt.mdc = cdk_calloc (1, sizeof *pkt->pkt.mdc);
|
||||
if (!pkt->pkt.mdc)
|
||||
return CDK_Out_Of_Core;
|
||||
break;
|
||||
|
||||
case CDK_PKT_ENCRYPTED_MDC:
|
||||
case CDK_PKT_ENCRYPTED:
|
||||
pkt->pkt.symkey_enc = cdk_calloc (1, sizeof *pkt->pkt.symkey_enc);
|
||||
if (!pkt->pkt.symkey_enc)
|
||||
return CDK_Out_Of_Core;
|
||||
break;
|
||||
|
||||
case CDK_PKT_ONEPASS_SIG:
|
||||
pkt->pkt.onepass_sig = cdk_calloc (1, sizeof *pkt->pkt.onepass_sig);
|
||||
if (!pkt->pkt.onepass_sig)
|
||||
return CDK_Out_Of_Core;
|
||||
break;
|
||||
|
||||
case CDK_PKT_LITERAL:
|
||||
/* FIXME: We would need the size of the file name to allocate extra
|
||||
bytes, otherwise the result would be useless. */
|
||||
pkt->pkt.literal = cdk_calloc (1, sizeof *pkt->pkt.literal);
|
||||
if (!pkt->pkt.literal)
|
||||
return CDK_Out_Of_Core;
|
||||
break;
|
||||
}
|
||||
pkt->pkttype = pkttype;
|
||||
*r_pkt = pkt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
cdk_prefitem_t
|
||||
_cdk_copy_prefs (const cdk_prefitem_t prefs)
|
||||
{
|
||||
size_t n = 0;
|
||||
struct cdk_prefitem_s *new_prefs;
|
||||
|
||||
if (!prefs)
|
||||
return NULL;
|
||||
|
||||
for (n = 0; prefs[n].type; n++)
|
||||
;
|
||||
new_prefs = cdk_calloc (1, sizeof *new_prefs * (n + 1));
|
||||
if (!new_prefs)
|
||||
return NULL;
|
||||
for (n = 0; prefs[n].type; n++)
|
||||
{
|
||||
new_prefs[n].type = prefs[n].type;
|
||||
new_prefs[n].value = prefs[n].value;
|
||||
}
|
||||
new_prefs[n].type = CDK_PREFTYPE_NONE;
|
||||
new_prefs[n].value = 0;
|
||||
return new_prefs;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
_cdk_copy_userid (cdk_pkt_userid_t * dst, cdk_pkt_userid_t src)
|
||||
{
|
||||
cdk_pkt_userid_t u;
|
||||
|
||||
if (!dst || !src)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
*dst = NULL;
|
||||
u = cdk_calloc (1, sizeof *u + strlen (src->name) + 1);
|
||||
if (!u)
|
||||
return CDK_Out_Of_Core;
|
||||
memcpy (u, src, sizeof *u);
|
||||
memcpy (u->name, src->name, strlen (src->name));
|
||||
u->prefs = _cdk_copy_prefs (src->prefs);
|
||||
if (src->selfsig)
|
||||
_cdk_copy_signature (&u->selfsig, src->selfsig);
|
||||
*dst = u;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
_cdk_copy_pubkey (cdk_pkt_pubkey_t * dst, cdk_pkt_pubkey_t src)
|
||||
{
|
||||
cdk_pkt_pubkey_t k;
|
||||
int i;
|
||||
|
||||
if (!dst || !src)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
*dst = NULL;
|
||||
k = cdk_calloc (1, sizeof *k);
|
||||
if (!k)
|
||||
return CDK_Out_Of_Core;
|
||||
memcpy (k, src, sizeof *k);
|
||||
if (src->uid)
|
||||
_cdk_copy_userid (&k->uid, src->uid);
|
||||
if (src->prefs)
|
||||
k->prefs = _cdk_copy_prefs (src->prefs);
|
||||
for (i = 0; i < cdk_pk_get_npkey (src->pubkey_algo); i++)
|
||||
k->mpi[i] = gcry_mpi_copy (src->mpi[i]);
|
||||
*dst = k;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
_cdk_copy_seckey (cdk_pkt_seckey_t * dst, cdk_pkt_seckey_t src)
|
||||
{
|
||||
cdk_pkt_seckey_t k;
|
||||
int i;
|
||||
|
||||
if (!dst || !src)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
*dst = NULL;
|
||||
k = cdk_calloc (1, sizeof *k);
|
||||
if (!k)
|
||||
return CDK_Out_Of_Core;
|
||||
memcpy (k, src, sizeof *k);
|
||||
_cdk_copy_pubkey (&k->pk, src->pk);
|
||||
|
||||
if (src->encdata)
|
||||
{
|
||||
k->encdata = cdk_calloc (1, src->enclen + 1);
|
||||
if (!k->encdata)
|
||||
return CDK_Out_Of_Core;
|
||||
memcpy (k->encdata, src->encdata, src->enclen);
|
||||
}
|
||||
|
||||
_cdk_s2k_copy (&k->protect.s2k, src->protect.s2k);
|
||||
|
||||
for (i = 0; i < cdk_pk_get_nskey (src->pubkey_algo); i++)
|
||||
{
|
||||
k->mpi[i] = gcry_mpi_copy (src->mpi[i]);
|
||||
gcry_mpi_set_flag (k->mpi[i], GCRYMPI_FLAG_SECURE);
|
||||
}
|
||||
|
||||
*dst = k;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
_cdk_copy_pk_to_sk (cdk_pkt_pubkey_t pk, cdk_pkt_seckey_t sk)
|
||||
{
|
||||
if (!pk || !sk)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
sk->version = pk->version;
|
||||
sk->expiredate = pk->expiredate;
|
||||
sk->pubkey_algo = pk->pubkey_algo;
|
||||
sk->has_expired = pk->has_expired;
|
||||
sk->is_revoked = pk->is_revoked;
|
||||
sk->main_keyid[0] = pk->main_keyid[0];
|
||||
sk->main_keyid[1] = pk->main_keyid[1];
|
||||
sk->keyid[0] = pk->keyid[0];
|
||||
sk->keyid[1] = pk->keyid[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
_cdk_copy_signature (cdk_pkt_signature_t * dst, cdk_pkt_signature_t src)
|
||||
{
|
||||
cdk_pkt_signature_t s;
|
||||
|
||||
if (!dst || !src)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
*dst = NULL;
|
||||
s = cdk_calloc (1, sizeof *s);
|
||||
if (!s)
|
||||
return CDK_Out_Of_Core;
|
||||
memcpy (s, src, sizeof *src);
|
||||
_cdk_subpkt_copy (&s->hashed, src->hashed);
|
||||
_cdk_subpkt_copy (&s->unhashed, src->unhashed);
|
||||
/* FIXME: Copy MPI parts */
|
||||
*dst = s;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
_cdk_pubkey_compare (cdk_pkt_pubkey_t a, cdk_pkt_pubkey_t b)
|
||||
{
|
||||
int na, nb, i;
|
||||
|
||||
if (a->timestamp != b->timestamp || a->pubkey_algo != b->pubkey_algo)
|
||||
return -1;
|
||||
if (a->version < 4 && a->expiredate != b->expiredate)
|
||||
return -1;
|
||||
na = cdk_pk_get_npkey (a->pubkey_algo);
|
||||
nb = cdk_pk_get_npkey (b->pubkey_algo);
|
||||
if (na != nb)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < na; i++)
|
||||
{
|
||||
if (gcry_mpi_cmp (a->mpi[i], b->mpi[i]))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_subpkt_free:
|
||||
* @ctx: the sub packet node to free
|
||||
*
|
||||
* Release the context.
|
||||
**/
|
||||
void
|
||||
cdk_subpkt_free (cdk_subpkt_t ctx)
|
||||
{
|
||||
cdk_subpkt_t s;
|
||||
|
||||
while (ctx)
|
||||
{
|
||||
s = ctx->next;
|
||||
cdk_free (ctx);
|
||||
ctx = s;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_subpkt_find:
|
||||
* @ctx: the sub packet node
|
||||
* @type: the packet type to find
|
||||
*
|
||||
* Find the given packet type in the node. If no packet with this
|
||||
* type was found, return null otherwise pointer to the node.
|
||||
**/
|
||||
cdk_subpkt_t
|
||||
cdk_subpkt_find (cdk_subpkt_t ctx, size_t type)
|
||||
{
|
||||
return cdk_subpkt_find_nth (ctx, type, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdk_subpkt_type_count:
|
||||
* @ctx: The sub packet context
|
||||
* @type: The sub packet type.
|
||||
*
|
||||
* Return the amount of sub packets with this type.
|
||||
**/
|
||||
size_t
|
||||
cdk_subpkt_type_count (cdk_subpkt_t ctx, size_t type)
|
||||
{
|
||||
cdk_subpkt_t s;
|
||||
size_t count;
|
||||
|
||||
count = 0;
|
||||
for (s = ctx; s; s = s->next)
|
||||
{
|
||||
if (s->type == type)
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_subpkt_find_nth:
|
||||
* @ctx: The sub packet context
|
||||
* @type: The sub packet type
|
||||
* @index: The nth packet to retrieve, 0 means the first
|
||||
*
|
||||
* Return the nth sub packet of the given type.
|
||||
**/
|
||||
cdk_subpkt_t
|
||||
cdk_subpkt_find_nth (cdk_subpkt_t ctx, size_t type, size_t idx)
|
||||
{
|
||||
cdk_subpkt_t s;
|
||||
size_t pos;
|
||||
|
||||
pos = 0;
|
||||
for (s = ctx; s; s = s->next)
|
||||
{
|
||||
if (s->type == type && pos++ == idx)
|
||||
return s;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_subpkt_new:
|
||||
* @size: the size of the new context
|
||||
*
|
||||
* Create a new sub packet node with the size of @size.
|
||||
**/
|
||||
cdk_subpkt_t
|
||||
cdk_subpkt_new (size_t size)
|
||||
{
|
||||
cdk_subpkt_t s;
|
||||
|
||||
if (!size)
|
||||
return NULL;
|
||||
s = cdk_calloc (1, sizeof *s + size + 1);
|
||||
if (!s)
|
||||
return NULL;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_subpkt_get_data:
|
||||
* @ctx: the sub packet node
|
||||
* @r_type: pointer store the packet type
|
||||
* @r_nbytes: pointer to store the packet size
|
||||
*
|
||||
* Extract the data from the given sub packet. The type is returned
|
||||
* in @r_type and the size in @r_nbytes.
|
||||
**/
|
||||
const byte *
|
||||
cdk_subpkt_get_data (cdk_subpkt_t ctx, size_t * r_type, size_t * r_nbytes)
|
||||
{
|
||||
if (!ctx || !r_nbytes)
|
||||
return NULL;
|
||||
if (r_type)
|
||||
*r_type = ctx->type;
|
||||
*r_nbytes = ctx->size;
|
||||
return ctx->d;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_subpkt_add:
|
||||
* @root: the root node
|
||||
* @node: the node to add
|
||||
*
|
||||
* Add the node in @node to the root node @root.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_subpkt_add (cdk_subpkt_t root, cdk_subpkt_t node)
|
||||
{
|
||||
cdk_subpkt_t n1;
|
||||
|
||||
if (!root)
|
||||
return CDK_Inv_Value;
|
||||
for (n1 = root; n1->next; n1 = n1->next)
|
||||
;
|
||||
n1->next = node;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
byte *
|
||||
_cdk_subpkt_get_array (cdk_subpkt_t s, int count, size_t * r_nbytes)
|
||||
{
|
||||
cdk_subpkt_t list;
|
||||
byte *buf;
|
||||
size_t n, nbytes;
|
||||
|
||||
if (!s)
|
||||
{
|
||||
if (r_nbytes)
|
||||
*r_nbytes = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (n = 0, list = s; list; list = list->next)
|
||||
{
|
||||
n++; /* type */
|
||||
n += list->size;
|
||||
if (list->size < 192)
|
||||
n++;
|
||||
else if (list->size < 8384)
|
||||
n += 2;
|
||||
else
|
||||
n += 5;
|
||||
}
|
||||
buf = cdk_calloc (1, n + 1);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
n = 0;
|
||||
for (list = s; list; list = list->next)
|
||||
{
|
||||
nbytes = 1 + list->size; /* type */
|
||||
if (nbytes < 192)
|
||||
buf[n++] = nbytes;
|
||||
else if (nbytes < 8384)
|
||||
{
|
||||
buf[n++] = nbytes / 256 + 192;
|
||||
buf[n++] = nbytes % 256;
|
||||
}
|
||||
else
|
||||
{
|
||||
buf[n++] = 0xFF;
|
||||
buf[n++] = nbytes >> 24;
|
||||
buf[n++] = nbytes >> 16;
|
||||
buf[n++] = nbytes >> 8;
|
||||
buf[n++] = nbytes;
|
||||
}
|
||||
buf[n++] = list->type;
|
||||
memcpy (buf + n, list->d, list->size);
|
||||
n += list->size;
|
||||
}
|
||||
|
||||
if (count)
|
||||
{
|
||||
cdk_free (buf);
|
||||
buf = NULL;
|
||||
}
|
||||
if (r_nbytes)
|
||||
*r_nbytes = n;
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
_cdk_subpkt_copy (cdk_subpkt_t * r_dst, cdk_subpkt_t src)
|
||||
{
|
||||
cdk_subpkt_t root, p, node;
|
||||
|
||||
if (!src || !r_dst)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
root = NULL;
|
||||
for (p = src; p; p = p->next)
|
||||
{
|
||||
node = cdk_subpkt_new (p->size);
|
||||
if (node)
|
||||
{
|
||||
memcpy (node->d, p->d, p->size);
|
||||
node->type = p->type;
|
||||
node->size = p->size;
|
||||
}
|
||||
if (!root)
|
||||
root = node;
|
||||
else
|
||||
cdk_subpkt_add (root, node);
|
||||
}
|
||||
*r_dst = root;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_subpkt_init:
|
||||
* @node: the sub packet node
|
||||
* @type: type of the packet which data should be initialized
|
||||
* @buf: the buffer with the actual data
|
||||
* @buflen: the size of the data
|
||||
*
|
||||
* Set the packet data of the given root and set the type of it.
|
||||
**/
|
||||
void
|
||||
cdk_subpkt_init (cdk_subpkt_t node, size_t type,
|
||||
const void *buf, size_t buflen)
|
||||
{
|
||||
if (!node)
|
||||
return;
|
||||
node->type = type;
|
||||
node->size = buflen;
|
||||
memcpy (node->d, buf, buflen);
|
||||
}
|
||||
|
||||
|
||||
/* FIXME: We need to think of a public interface for it. */
|
||||
const byte *
|
||||
cdk_key_desig_revoker_walk (cdk_desig_revoker_t root,
|
||||
cdk_desig_revoker_t * ctx,
|
||||
int *r_class, int *r_algid)
|
||||
{
|
||||
cdk_desig_revoker_t n;
|
||||
|
||||
if (!*ctx)
|
||||
{
|
||||
*ctx = root;
|
||||
n = root;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = (*ctx)->next;
|
||||
*ctx = n;
|
||||
}
|
||||
|
||||
if (n && r_class && r_algid)
|
||||
{
|
||||
*r_class = n->r_class;
|
||||
*r_algid = n->algid;
|
||||
}
|
||||
|
||||
return n ? n->fpr : NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_subpkt_find_next:
|
||||
* @root: the base where to begin the iteration
|
||||
* @type: the type to find or 0 for the next node.
|
||||
*
|
||||
* Try to find the next node after @root with type.
|
||||
* If type is 0, the next node will be returned.
|
||||
**/
|
||||
cdk_subpkt_t
|
||||
cdk_subpkt_find_next (cdk_subpkt_t root, size_t type)
|
||||
{
|
||||
cdk_subpkt_t node;
|
||||
|
||||
for (node = root->next; node; node = node->next)
|
||||
{
|
||||
if (!type)
|
||||
return node;
|
||||
else if (node->type == type)
|
||||
return node;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -1,1169 +0,0 @@
|
||||
/* opencdk.h - Open Crypto Development Kit (OpenCDK)
|
||||
* Copyright (C) 2001, 2002, 2003, 2007 Timo Schulz
|
||||
* Copyright (C) 2006, 2007 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
|
||||
#ifndef OPENCDK_H
|
||||
#define OPENCDK_H
|
||||
|
||||
#include <stddef.h> /* for size_t */
|
||||
#include <stdarg.h>
|
||||
#include <gcrypt.h>
|
||||
|
||||
/* The OpenCDK version as a string. */
|
||||
#define OPENCDK_VERSION "0.6.6"
|
||||
|
||||
/* The OpenCDK version as integer components major.minor.path */
|
||||
#define OPENCDK_VERSION_MAJOR 0
|
||||
#define OPENCDK_VERSION_MINOR 6
|
||||
#define OPENCDK_VERSION_PATCH 6
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#if 0
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* General contexts */
|
||||
|
||||
/* 'Session' handle to support the various options and run-time
|
||||
information. */
|
||||
struct cdk_ctx_s;
|
||||
typedef struct cdk_ctx_s *cdk_ctx_t;
|
||||
|
||||
/* A generic context to store list of strings. */
|
||||
struct cdk_strlist_s;
|
||||
typedef struct cdk_strlist_s *cdk_strlist_t;
|
||||
|
||||
/* Context used to list keys of a keyring. */
|
||||
struct cdk_listkey_s;
|
||||
typedef struct cdk_listkey_s *cdk_listkey_t;
|
||||
|
||||
/* Opaque Data Encryption Key (DEK) context. */
|
||||
struct cdk_dek_s;
|
||||
typedef struct cdk_dek_s *cdk_dek_t;
|
||||
|
||||
/* Opaque String to Key (S2K) handle. */
|
||||
struct cdk_s2k_s;
|
||||
typedef struct cdk_s2k_s *cdk_s2k_t;
|
||||
|
||||
/* Abstract I/O object, a stream, which is used for most operations. */
|
||||
struct cdk_stream_s;
|
||||
typedef struct cdk_stream_s *cdk_stream_t;
|
||||
|
||||
/* Opaque handle for the user ID preferences. */
|
||||
struct cdk_prefitem_s;
|
||||
typedef struct cdk_prefitem_s *cdk_prefitem_t;
|
||||
|
||||
/* Node to store a single key node packet. */
|
||||
struct cdk_kbnode_s;
|
||||
typedef struct cdk_kbnode_s *cdk_kbnode_t;
|
||||
|
||||
/* Key database handle. */
|
||||
struct cdk_keydb_hd_s;
|
||||
typedef struct cdk_keydb_hd_s *cdk_keydb_hd_t;
|
||||
|
||||
/* Context to store a list of recipient keys. */
|
||||
struct cdk_keylist_s;
|
||||
typedef struct cdk_keylist_s *cdk_keylist_t;
|
||||
|
||||
/* Context to encapsulate a single sub packet of a signature. */
|
||||
struct cdk_subpkt_s;
|
||||
typedef struct cdk_subpkt_s *cdk_subpkt_t;
|
||||
|
||||
/* Context used to generate key pairs. */
|
||||
struct cdk_keygen_ctx_s;
|
||||
typedef struct cdk_keygen_ctx_s *cdk_keygen_ctx_t;
|
||||
|
||||
/* Handle for a single designated revoker. */
|
||||
struct cdk_desig_revoker_s;
|
||||
typedef struct cdk_desig_revoker_s *cdk_desig_revoker_t;
|
||||
|
||||
/* Alias for backward compatibility. */
|
||||
typedef gcry_mpi_t cdk_mpi_t;
|
||||
|
||||
|
||||
/* All valid error constants. */
|
||||
typedef enum {
|
||||
CDK_EOF = -1,
|
||||
CDK_Success = 0,
|
||||
CDK_General_Error = 1,
|
||||
CDK_File_Error = 2,
|
||||
CDK_Bad_Sig = 3,
|
||||
CDK_Inv_Packet = 4,
|
||||
CDK_Inv_Algo = 5,
|
||||
CDK_Not_Implemented = 6,
|
||||
CDK_Armor_Error = 8,
|
||||
CDK_Armor_CRC_Error = 9,
|
||||
CDK_MPI_Error = 10,
|
||||
CDK_Inv_Value = 11,
|
||||
CDK_Error_No_Key = 12,
|
||||
CDK_Chksum_Error = 13,
|
||||
CDK_Time_Conflict = 14,
|
||||
CDK_Zlib_Error = 15,
|
||||
CDK_Weak_Key = 16,
|
||||
CDK_Out_Of_Core = 17,
|
||||
CDK_Wrong_Seckey = 18,
|
||||
CDK_Bad_MDC = 19,
|
||||
CDK_Inv_Mode = 20,
|
||||
CDK_Error_No_Keyring = 21,
|
||||
CDK_Wrong_Format = 22,
|
||||
CDK_Inv_Packet_Ver = 23,
|
||||
CDK_Too_Short = 24,
|
||||
CDK_Unusable_Key = 25,
|
||||
CDK_No_Data = 26,
|
||||
CDK_No_Passphrase = 27,
|
||||
CDK_Network_Error = 28
|
||||
} cdk_error_t;
|
||||
|
||||
|
||||
enum cdk_control_flags {
|
||||
CDK_CTLF_SET = 0, /* Value to set an option */
|
||||
CDK_CTLF_GET = 1, /* Value to get an option */
|
||||
CDK_CTL_DIGEST = 10, /* Option to set the digest algorithm. */
|
||||
CDK_CTL_CIPHER = 11, /* Option to set the cipher algorithm. */
|
||||
CDK_CTL_ARMOR = 12, /* Option to enable armor output. */
|
||||
CDK_CTL_COMPRESS = 13, /* Option to enable compression. */
|
||||
CDK_CTL_COMPAT = 14, /* Option to switch in compat mode. */
|
||||
CDK_CTL_OVERWRITE = 15, /* Option to enable file overwritting. */
|
||||
CDK_CTL_S2K = 16, /* Option to set S2K values. */
|
||||
CDK_CTL_FORCE_DIGEST = 19, /* Force the use of a digest algorithm. */
|
||||
CDK_CTL_BLOCKMODE_ON = 20 /* Enable partial body lengths */
|
||||
};
|
||||
|
||||
|
||||
/* Specifies all valid log levels. */
|
||||
enum cdk_log_level_t {
|
||||
CDK_LOG_NONE = 0, /* No log message will be shown. */
|
||||
CDK_LOG_INFO = 1,
|
||||
CDK_LOG_DEBUG = 2,
|
||||
CDK_LOG_DEBUG_PKT = 3
|
||||
};
|
||||
|
||||
|
||||
/* All valid compression algorithms in OpenPGP */
|
||||
enum cdk_compress_algo_t {
|
||||
CDK_COMPRESS_NONE = 0,
|
||||
CDK_COMPRESS_ZIP = 1,
|
||||
CDK_COMPRESS_ZLIB = 2,
|
||||
CDK_COMPRESS_BZIP2 = 3 /* Not supported in this version */
|
||||
};
|
||||
|
||||
|
||||
/* All valid public key algorithms valid in OpenPGP */
|
||||
enum cdk_pubkey_algo_t {
|
||||
CDK_PK_RSA = 1,
|
||||
CDK_PK_RSA_E = 2, /* RSA-E and RSA-S are deprecated use RSA instead */
|
||||
CDK_PK_RSA_S = 3, /* and use the key flags in the self signatures. */
|
||||
CDK_PK_ELG_E = 16,
|
||||
CDK_PK_DSA = 17
|
||||
};
|
||||
|
||||
|
||||
/* All valid message digest algorithms in OpenPGP. */
|
||||
enum cdk_digest_algo_t {
|
||||
CDK_MD_NONE = 0,
|
||||
CDK_MD_MD5 = 1,
|
||||
CDK_MD_SHA1 = 2,
|
||||
CDK_MD_RMD160 = 3,
|
||||
CDK_MD_SHA256 = 8,
|
||||
CDK_MD_SHA384 = 9,
|
||||
CDK_MD_SHA512 = 10,
|
||||
CDK_MD_SHA224 = 11 /* This algorithm is NOT available. */
|
||||
};
|
||||
|
||||
|
||||
/* All valid symmetric cipher algorithms in OpenPGP */
|
||||
enum cdk_cipher_algo_t {
|
||||
CDK_CIPHER_NONE = 0,
|
||||
CDK_CIPHER_IDEA = 1, /* This algorithm is NOT available */
|
||||
CDK_CIPHER_3DES = 2,
|
||||
CDK_CIPHER_CAST5 = 3,
|
||||
CDK_CIPHER_BLOWFISH = 4,
|
||||
CDK_CIPHER_AES = 7,
|
||||
CDK_CIPHER_AES192 = 8,
|
||||
CDK_CIPHER_AES256 = 9,
|
||||
CDK_CIPHER_TWOFISH = 10
|
||||
};
|
||||
|
||||
|
||||
/* The valid 'String-To-Key' modes */
|
||||
enum cdk_s2k_type_t {
|
||||
CDK_S2K_SIMPLE = 0,
|
||||
CDK_S2K_SALTED = 1,
|
||||
CDK_S2K_ITERSALTED = 3
|
||||
};
|
||||
|
||||
|
||||
/* The different kind of user ID preferences. */
|
||||
enum cdk_pref_type_t {
|
||||
CDK_PREFTYPE_NONE = 0,
|
||||
CDK_PREFTYPE_SYM = 1, /* Symmetric ciphers */
|
||||
CDK_PREFTYPE_HASH = 2, /* Message digests */
|
||||
CDK_PREFTYPE_ZIP = 3 /* Compression algorithms */
|
||||
};
|
||||
|
||||
|
||||
/* All valid sub packet types. */
|
||||
enum cdk_sig_subpacket_t {
|
||||
CDK_SIGSUBPKT_NONE = 0,
|
||||
CDK_SIGSUBPKT_SIG_CREATED = 2,
|
||||
CDK_SIGSUBPKT_SIG_EXPIRE = 3,
|
||||
CDK_SIGSUBPKT_EXPORTABLE = 4,
|
||||
CDK_SIGSUBPKT_TRUST = 5,
|
||||
CDK_SIGSUBPKT_REGEXP = 6,
|
||||
CDK_SIGSUBPKT_REVOCABLE = 7,
|
||||
CDK_SIGSUBPKT_KEY_EXPIRE = 9,
|
||||
CDK_SIGSUBPKT_PREFS_SYM = 11,
|
||||
CDK_SIGSUBPKT_REV_KEY = 12,
|
||||
CDK_SIGSUBPKT_ISSUER = 16,
|
||||
CDK_SIGSUBPKT_NOTATION = 20,
|
||||
CDK_SIGSUBPKT_PREFS_HASH = 21,
|
||||
CDK_SIGSUBPKT_PREFS_ZIP = 22,
|
||||
CDK_SIGSUBPKT_KS_FLAGS = 23,
|
||||
CDK_SIGSUBPKT_PREF_KS = 24,
|
||||
CDK_SIGSUBPKT_PRIMARY_UID = 25,
|
||||
CDK_SIGSUBPKT_POLICY = 26,
|
||||
CDK_SIGSUBPKT_KEY_FLAGS = 27,
|
||||
CDK_SIGSUBPKT_SIGNERS_UID = 28,
|
||||
CDK_SIGSUBPKT_REVOC_REASON = 29,
|
||||
CDK_SIGSUBPKT_FEATURES = 30
|
||||
};
|
||||
|
||||
|
||||
/* All valid armor types. */
|
||||
enum cdk_armor_type_t {
|
||||
CDK_ARMOR_MESSAGE = 0,
|
||||
CDK_ARMOR_PUBKEY = 1,
|
||||
CDK_ARMOR_SECKEY = 2,
|
||||
CDK_ARMOR_SIGNATURE = 3,
|
||||
CDK_ARMOR_CLEARSIG = 4
|
||||
};
|
||||
|
||||
enum cdk_keydb_flag_t {
|
||||
/* Valid database search modes */
|
||||
CDK_DBSEARCH_EXACT = 1, /* Exact string search */
|
||||
CDK_DBSEARCH_SUBSTR = 2, /* Sub string search */
|
||||
CDK_DBSEARCH_SHORT_KEYID = 3, /* 32-bit keyid search */
|
||||
CDK_DBSEARCH_KEYID = 4, /* 64-bit keyid search */
|
||||
CDK_DBSEARCH_FPR = 5, /* 160-bit fingerprint search */
|
||||
CDK_DBSEARCH_NEXT = 6, /* Enumerate all keys */
|
||||
CDK_DBSEARCH_AUTO = 7, /* Try to classify the string */
|
||||
/* Valid database types */
|
||||
CDK_DBTYPE_PK_KEYRING = 100, /* A file with one or more public keys */
|
||||
CDK_DBTYPE_SK_KEYRING = 101, /* A file with one or more secret keys */
|
||||
CDK_DBTYPE_DATA = 102, /* A buffer with at least one public key */
|
||||
CDK_DBTYPE_STREAM = 103 /* A stream is used to read keys from */
|
||||
};
|
||||
|
||||
|
||||
/* All valid modes for cdk_data_transform() */
|
||||
enum cdk_crypto_mode_t {
|
||||
CDK_CRYPTYPE_NONE = 0,
|
||||
CDK_CRYPTYPE_ENCRYPT = 1,
|
||||
CDK_CRYPTYPE_DECRYPT = 2,
|
||||
CDK_CRYPTYPE_SIGN = 3,
|
||||
CDK_CRYPTYPE_VERIFY = 4,
|
||||
CDK_CRYPTYPE_EXPORT = 5,
|
||||
CDK_CRYPTYPE_IMPORT = 6
|
||||
};
|
||||
|
||||
|
||||
/* A list of valid public key usages. */
|
||||
enum cdk_key_usage_t {
|
||||
CDK_KEY_USG_ENCR = 1, /* Key can be used for encryption. */
|
||||
CDK_KEY_USG_SIGN = 2, /* Key can be used for signing and certifying. */
|
||||
CDK_KEY_USG_AUTH = 4 /* Key can be used for authentication. */
|
||||
};
|
||||
|
||||
|
||||
/* Valid flags for keys. */
|
||||
enum cdk_key_flag_t {
|
||||
CDK_KEY_VALID = 0,
|
||||
CDK_KEY_INVALID = 1, /* Missing or wrong self signature */
|
||||
CDK_KEY_EXPIRED = 2, /* Key is expired. */
|
||||
CDK_KEY_REVOKED = 4, /* Key has been revoked. */
|
||||
CDK_KEY_NOSIGNER = 8
|
||||
};
|
||||
|
||||
|
||||
/* Trust values and flags for keys and user IDs */
|
||||
enum cdk_trust_flag_t {
|
||||
CDK_TRUST_UNKNOWN = 0,
|
||||
CDK_TRUST_EXPIRED = 1,
|
||||
CDK_TRUST_UNDEFINED = 2,
|
||||
CDK_TRUST_NEVER = 3,
|
||||
CDK_TRUST_MARGINAL = 4,
|
||||
CDK_TRUST_FULLY = 5,
|
||||
CDK_TRUST_ULTIMATE = 6,
|
||||
/* trust flags */
|
||||
CDK_TFLAG_REVOKED = 32,
|
||||
CDK_TFLAG_SUB_REVOKED = 64,
|
||||
CDK_TFLAG_DISABLED = 128
|
||||
};
|
||||
|
||||
|
||||
/* Signature states and the signature modes. */
|
||||
enum cdk_signature_stat_t {
|
||||
/* Signature status */
|
||||
CDK_SIGSTAT_NONE = 0,
|
||||
CDK_SIGSTAT_GOOD = 1,
|
||||
CDK_SIGSTAT_BAD = 2,
|
||||
CDK_SIGSTAT_NOKEY = 3,
|
||||
CDK_SIGSTAT_VALID = 4, /* True if made with a valid key. */
|
||||
/* FIXME: We need indicators for revoked/expires signatures. */
|
||||
|
||||
/* Signature modes */
|
||||
CDK_SIGMODE_NORMAL = 100,
|
||||
CDK_SIGMODE_DETACHED = 101,
|
||||
CDK_SIGMODE_CLEAR = 102
|
||||
};
|
||||
|
||||
|
||||
/* Key flags. */
|
||||
typedef enum {
|
||||
CDK_FLAG_KEY_REVOKED = 256,
|
||||
CDK_FLAG_KEY_EXPIRED = 512,
|
||||
CDK_FLAG_SIG_EXPIRED = 1024
|
||||
} cdk_key_flags_t;
|
||||
|
||||
|
||||
/* Possible format for the literal data. */
|
||||
typedef enum {
|
||||
CDK_LITFMT_BINARY = 0,
|
||||
CDK_LITFMT_TEXT = 1,
|
||||
CDK_LITFMT_UNICODE= 2
|
||||
} cdk_lit_format_t;
|
||||
|
||||
/* Valid OpenPGP packet types and their IDs */
|
||||
typedef enum {
|
||||
CDK_PKT_RESERVED = 0,
|
||||
CDK_PKT_PUBKEY_ENC = 1,
|
||||
CDK_PKT_SIGNATURE = 2,
|
||||
CDK_PKT_SYMKEY_ENC = 3,
|
||||
CDK_PKT_ONEPASS_SIG = 4,
|
||||
CDK_PKT_SECRET_KEY = 5,
|
||||
CDK_PKT_PUBLIC_KEY = 6,
|
||||
CDK_PKT_SECRET_SUBKEY = 7,
|
||||
CDK_PKT_COMPRESSED = 8,
|
||||
CDK_PKT_ENCRYPTED = 9,
|
||||
CDK_PKT_MARKER = 10,
|
||||
CDK_PKT_LITERAL = 11,
|
||||
CDK_PKT_RING_TRUST = 12,
|
||||
CDK_PKT_USER_ID = 13,
|
||||
CDK_PKT_PUBLIC_SUBKEY = 14,
|
||||
CDK_PKT_OLD_COMMENT = 16,
|
||||
CDK_PKT_ATTRIBUTE = 17,
|
||||
CDK_PKT_ENCRYPTED_MDC = 18,
|
||||
CDK_PKT_MDC = 19
|
||||
} cdk_packet_type_t;
|
||||
|
||||
/* Define the maximal number of multiprecion integers for
|
||||
a public key. */
|
||||
#define MAX_CDK_PK_PARTS 4
|
||||
|
||||
/* Define the maximal number of multiprecision integers for
|
||||
a signature/encrypted blob issued by a secret key. */
|
||||
#define MAX_CDK_DATA_PARTS 2
|
||||
|
||||
|
||||
/* Helper macro to figure out if the packet is encrypted */
|
||||
#define CDK_PKT_IS_ENCRYPTED(pkttype) (\
|
||||
((pkttype)==CDK_PKT_ENCRYPTED_MDC) \
|
||||
|| ((pkttype)==CDK_PKT_ENCRYPTED))
|
||||
|
||||
|
||||
struct cdk_pkt_signature_s {
|
||||
unsigned char version;
|
||||
unsigned char sig_class;
|
||||
unsigned int timestamp;
|
||||
unsigned int expiredate;
|
||||
unsigned int keyid[2];
|
||||
unsigned char pubkey_algo;
|
||||
unsigned char digest_algo;
|
||||
unsigned char digest_start[2];
|
||||
unsigned short hashed_size;
|
||||
cdk_subpkt_t hashed;
|
||||
unsigned short unhashed_size;
|
||||
cdk_subpkt_t unhashed;
|
||||
gcry_mpi_t mpi[MAX_CDK_DATA_PARTS];
|
||||
cdk_desig_revoker_t revkeys;
|
||||
struct {
|
||||
unsigned exportable:1;
|
||||
unsigned revocable:1;
|
||||
unsigned policy_url:1;
|
||||
unsigned notation:1;
|
||||
unsigned expired:1;
|
||||
unsigned checked:1;
|
||||
unsigned valid:1;
|
||||
unsigned missing_key:1;
|
||||
} flags;
|
||||
unsigned int key[2]; /* only valid for key signatures */
|
||||
};
|
||||
typedef struct cdk_pkt_signature_s *cdk_pkt_signature_t;
|
||||
|
||||
|
||||
struct cdk_pkt_userid_s {
|
||||
unsigned int len;
|
||||
unsigned is_primary:1;
|
||||
unsigned is_revoked:1;
|
||||
unsigned mdc_feature:1;
|
||||
cdk_prefitem_t prefs;
|
||||
size_t prefs_size;
|
||||
unsigned char * attrib_img; /* Tag 17 if not null */
|
||||
size_t attrib_len;
|
||||
cdk_pkt_signature_t selfsig;
|
||||
char name[1];
|
||||
};
|
||||
typedef struct cdk_pkt_userid_s *cdk_pkt_userid_t;
|
||||
|
||||
|
||||
struct cdk_pkt_pubkey_s {
|
||||
unsigned char version;
|
||||
unsigned char pubkey_algo;
|
||||
unsigned char fpr[20];
|
||||
unsigned int keyid[2];
|
||||
unsigned int main_keyid[2];
|
||||
unsigned int timestamp;
|
||||
unsigned int expiredate;
|
||||
gcry_mpi_t mpi[MAX_CDK_PK_PARTS];
|
||||
unsigned is_revoked:1;
|
||||
unsigned is_invalid:1;
|
||||
unsigned has_expired:1;
|
||||
int pubkey_usage;
|
||||
cdk_pkt_userid_t uid;
|
||||
cdk_prefitem_t prefs;
|
||||
size_t prefs_size;
|
||||
cdk_desig_revoker_t revkeys;
|
||||
};
|
||||
typedef struct cdk_pkt_pubkey_s *cdk_pkt_pubkey_t;
|
||||
|
||||
/* Alias to define a generic public key context. */
|
||||
typedef cdk_pkt_pubkey_t cdk_pubkey_t;
|
||||
|
||||
|
||||
struct cdk_pkt_seckey_s {
|
||||
cdk_pkt_pubkey_t pk;
|
||||
unsigned int expiredate;
|
||||
int version;
|
||||
int pubkey_algo;
|
||||
unsigned int keyid[2];
|
||||
unsigned int main_keyid[2];
|
||||
unsigned char s2k_usage;
|
||||
struct {
|
||||
unsigned char algo;
|
||||
unsigned char sha1chk; /* SHA1 is used instead of a 16 bit checksum */
|
||||
cdk_s2k_t s2k;
|
||||
unsigned char iv[16];
|
||||
unsigned char ivlen;
|
||||
} protect;
|
||||
unsigned short csum;
|
||||
gcry_mpi_t mpi[MAX_CDK_PK_PARTS];
|
||||
unsigned char * encdata;
|
||||
size_t enclen;
|
||||
unsigned char is_protected;
|
||||
unsigned is_primary:1;
|
||||
unsigned has_expired:1;
|
||||
unsigned is_revoked:1;
|
||||
};
|
||||
typedef struct cdk_pkt_seckey_s *cdk_pkt_seckey_t;
|
||||
|
||||
/* Alias to define a generic secret key context. */
|
||||
typedef cdk_pkt_seckey_t cdk_seckey_t;
|
||||
|
||||
|
||||
struct cdk_pkt_onepass_sig_s {
|
||||
unsigned char version;
|
||||
unsigned int keyid[2];
|
||||
unsigned char sig_class;
|
||||
unsigned char digest_algo;
|
||||
unsigned char pubkey_algo;
|
||||
unsigned char last;
|
||||
};
|
||||
typedef struct cdk_pkt_onepass_sig_s * cdk_pkt_onepass_sig_t;
|
||||
|
||||
|
||||
struct cdk_pkt_pubkey_enc_s {
|
||||
unsigned char version;
|
||||
unsigned int keyid[2];
|
||||
int throw_keyid;
|
||||
unsigned char pubkey_algo;
|
||||
gcry_mpi_t mpi[MAX_CDK_DATA_PARTS];
|
||||
};
|
||||
typedef struct cdk_pkt_pubkey_enc_s * cdk_pkt_pubkey_enc_t;
|
||||
|
||||
|
||||
struct cdk_pkt_symkey_enc_s {
|
||||
unsigned char version;
|
||||
unsigned char cipher_algo;
|
||||
cdk_s2k_t s2k;
|
||||
unsigned char seskeylen;
|
||||
unsigned char seskey[32];
|
||||
};
|
||||
typedef struct cdk_pkt_symkey_enc_s *cdk_pkt_symkey_enc_t;
|
||||
|
||||
|
||||
struct cdk_pkt_encrypted_s {
|
||||
unsigned int len;
|
||||
int extralen;
|
||||
unsigned char mdc_method;
|
||||
cdk_stream_t buf;
|
||||
};
|
||||
typedef struct cdk_pkt_encrypted_s *cdk_pkt_encrypted_t;
|
||||
|
||||
|
||||
struct cdk_pkt_mdc_s {
|
||||
unsigned char hash[20];
|
||||
};
|
||||
typedef struct cdk_pkt_mdc_s *cdk_pkt_mdc_t;
|
||||
|
||||
|
||||
struct cdk_pkt_literal_s {
|
||||
unsigned int len;
|
||||
cdk_stream_t buf;
|
||||
int mode;
|
||||
unsigned int timestamp;
|
||||
int namelen;
|
||||
char name[1];
|
||||
};
|
||||
typedef struct cdk_pkt_literal_s *cdk_pkt_literal_t;
|
||||
|
||||
|
||||
struct cdk_pkt_compressed_s {
|
||||
unsigned int len;
|
||||
int algorithm;
|
||||
cdk_stream_t buf;
|
||||
};
|
||||
typedef struct cdk_pkt_compressed_s *cdk_pkt_compressed_t;
|
||||
|
||||
|
||||
/* Structure which represents a single OpenPGP packet. */
|
||||
struct cdk_packet_s {
|
||||
size_t pktlen; /* real packet length */
|
||||
size_t pktsize; /* length with all headers */
|
||||
int old_ctb; /* 1 if RFC1991 mode is used */
|
||||
cdk_packet_type_t pkttype;
|
||||
union {
|
||||
cdk_pkt_mdc_t mdc;
|
||||
cdk_pkt_userid_t user_id;
|
||||
cdk_pkt_pubkey_t public_key;
|
||||
cdk_pkt_seckey_t secret_key;
|
||||
cdk_pkt_signature_t signature;
|
||||
cdk_pkt_pubkey_enc_t pubkey_enc;
|
||||
cdk_pkt_symkey_enc_t symkey_enc;
|
||||
cdk_pkt_compressed_t compressed;
|
||||
cdk_pkt_encrypted_t encrypted;
|
||||
cdk_pkt_literal_t literal;
|
||||
cdk_pkt_onepass_sig_t onepass_sig;
|
||||
} pkt;
|
||||
};
|
||||
typedef struct cdk_packet_s *cdk_packet_t;
|
||||
|
||||
/* memory routines */
|
||||
typedef void (*cdk_log_fnc_t) (void *, int, const char *, va_list);
|
||||
|
||||
/* Set the log level. */
|
||||
void cdk_set_log_level (int lvl);
|
||||
|
||||
/* Set a custom log handler which is used for logging. */
|
||||
void cdk_set_log_handler (cdk_log_fnc_t logfnc, void *opaque);
|
||||
|
||||
/* Return a human readable error description of the given error coe. */
|
||||
const char* cdk_strerror (int ec);
|
||||
|
||||
/* Allow the user to set custom hooks for memory allocation.
|
||||
If this function is not used, the standard allocation functions
|
||||
will be used. Extra care must be taken for the 'secure' alloc
|
||||
function. */
|
||||
void cdk_set_malloc_hooks (void *(*new_alloc_func) (size_t n),
|
||||
void *(*new_alloc_secure_func) (size_t n),
|
||||
void *(*new_realloc_func) (void *p, size_t n),
|
||||
void *(*new_calloc_func) (size_t m, size_t n),
|
||||
void (*new_free_func) (void *));
|
||||
|
||||
/* Return 1 if the malloc hooks were already set via the function above. */
|
||||
int cdk_malloc_hook_initialized (void);
|
||||
|
||||
/* Standard memory wrapper. */
|
||||
void *cdk_malloc (size_t size);
|
||||
void *cdk_calloc (size_t n, size_t m);
|
||||
void *cdk_realloc (void * ptr, size_t size);
|
||||
void *cdk_salloc (size_t size, int clear);
|
||||
char *cdk_strdup (const char * ptr);
|
||||
void cdk_free (void * ptr);
|
||||
|
||||
/* Startup routines. */
|
||||
|
||||
/* This function has to be called before any other
|
||||
CDK function is executed. */
|
||||
void cdk_lib_startup (void);
|
||||
|
||||
/* This function should be called before the application exists
|
||||
to allow the lib to cleanup its internal structures. */
|
||||
void cdk_lib_shutdown (void);
|
||||
|
||||
/* Session handle routines */
|
||||
cdk_error_t cdk_handle_new (cdk_ctx_t *r_ctx);
|
||||
void cdk_handle_free (cdk_ctx_t c);
|
||||
|
||||
/* Set the key database handle for the given session handle.
|
||||
The type of the key db handle (public or secret) decides
|
||||
which session key db handle to use. */
|
||||
void cdk_handle_set_keydb (cdk_ctx_t hd, cdk_keydb_hd_t db);
|
||||
|
||||
/* Convenient function to avoid to open a key db first.
|
||||
The user can directly use the file name, the rest is
|
||||
done internally. */
|
||||
cdk_error_t cdk_handle_set_keyring (cdk_ctx_t hd, int type,
|
||||
const char *kringname);
|
||||
|
||||
/* Return keydb handle stored in the session handle. */
|
||||
cdk_keydb_hd_t cdk_handle_get_keydb (cdk_ctx_t hd, int type);
|
||||
int cdk_handle_control (cdk_ctx_t hd, int action, int cmd, ...);
|
||||
|
||||
/* Set a passphrase callback for the given session handle. */
|
||||
void cdk_handle_set_passphrase_cb (cdk_ctx_t hd,
|
||||
char *(*cb) (void *opa, const char *prompt),
|
||||
void *cb_value);
|
||||
|
||||
/* shortcuts for some controls */
|
||||
|
||||
/* Enable or disable armor output. */
|
||||
#define cdk_handle_set_armor(a, val) \
|
||||
cdk_handle_control ((a), CDK_CTLF_SET, CDK_CTL_ARMOR, (val))
|
||||
|
||||
/* Set the compression algorithm and level. 0 means disable compression. */
|
||||
#define cdk_handle_set_compress(a, algo, level) \
|
||||
cdk_handle_control ((a), CDK_CTLF_SET, CDK_CTL_COMPRESS, (algo), (level))
|
||||
|
||||
/* Activate partial bodies for the output. This is needed if the length
|
||||
of the data is not known in advance or for the use with sockets
|
||||
or pipes. */
|
||||
#define cdk_handle_set_blockmode(a, val) \
|
||||
cdk_handle_control ((a), CDK_CTLF_SET, CDK_CTL_BLOCKMODE_ON, (val))
|
||||
|
||||
/* Set the symmetric cipher for encryption. */
|
||||
#define cdk_handle_set_cipher(a, val) \
|
||||
cdk_handle_control ((a), CDK_CTLF_SET, CDK_CTL_CIPHER, (val))
|
||||
|
||||
/* Set the digest for the PK signing operation. */
|
||||
#define cdk_handle_set_digest(a, val) \
|
||||
cdk_handle_control ((a), CDK_CTLF_SET, CDK_CTL_DIGEST, (val))
|
||||
|
||||
/* Set the mode and the digest for the S2K operation. */
|
||||
#define cdk_handle_set_s2k(a, val1, val2) \
|
||||
cdk_handle_control ((a), CDK_CTLF_SET, CDK_CTL_S2K, (val1), (val2))
|
||||
|
||||
|
||||
/* This context holds all information of the verification process. */
|
||||
struct cdk_verify_result_s {
|
||||
int sig_ver; /* Version of the signature. */
|
||||
int sig_status; /* The status (GOOD, BAD) of the signature */
|
||||
int sig_flags; /* May contain expired or revoked flags */
|
||||
unsigned int keyid[2]; /* The issuer key ID */
|
||||
unsigned int created; /* Timestamp when the sig was created. */
|
||||
unsigned int expires;
|
||||
int pubkey_algo;
|
||||
int digest_algo;
|
||||
char *user_id; /* NULL or user ID which issued the signature. */
|
||||
char *policy_url; /* If set, the policy the sig was created under. */
|
||||
size_t sig_len; /* Size of the signature data inbits. */
|
||||
unsigned char *sig_data; /* Raw signature data. */
|
||||
};
|
||||
typedef struct cdk_verify_result_s *cdk_verify_result_t;
|
||||
|
||||
/* Return the verify result. Do not free the data. */
|
||||
cdk_verify_result_t cdk_handle_verify_get_result (cdk_ctx_t hd);
|
||||
|
||||
/* Raw packet routines. */
|
||||
|
||||
/* Allocate a new packet or a new packet with the given packet type. */
|
||||
cdk_error_t cdk_pkt_new (cdk_packet_t *r_pkt);
|
||||
cdk_error_t cdk_pkt_alloc (cdk_packet_t *r_pkt, int pkttype);
|
||||
|
||||
/* Only release the contents of the packet but not @PKT itself. */
|
||||
void cdk_pkt_free (cdk_packet_t pkt);
|
||||
|
||||
/* Release the packet contents and the packet structure @PKT itself. */
|
||||
void cdk_pkt_release (cdk_packet_t pkt);
|
||||
|
||||
/* Read or write the given output from or to the stream. */
|
||||
cdk_error_t cdk_pkt_read (cdk_stream_t inp, cdk_packet_t pkt);
|
||||
cdk_error_t cdk_pkt_write (cdk_stream_t out, cdk_packet_t pkt);
|
||||
|
||||
/* Sub packet routines */
|
||||
cdk_subpkt_t cdk_subpkt_new (size_t size);
|
||||
void cdk_subpkt_free (cdk_subpkt_t ctx);
|
||||
cdk_subpkt_t cdk_subpkt_find (cdk_subpkt_t ctx, size_t type);
|
||||
cdk_subpkt_t cdk_subpkt_find_next (cdk_subpkt_t root, size_t type);
|
||||
size_t cdk_subpkt_type_count (cdk_subpkt_t ctx, size_t type);
|
||||
cdk_subpkt_t cdk_subpkt_find_nth (cdk_subpkt_t ctx, size_t type, size_t index);
|
||||
cdk_error_t cdk_subpkt_add (cdk_subpkt_t root, cdk_subpkt_t node);
|
||||
const unsigned char * cdk_subpkt_get_data (cdk_subpkt_t ctx,
|
||||
size_t * r_type, size_t *r_nbytes);
|
||||
void cdk_subpkt_init (cdk_subpkt_t node, size_t type,
|
||||
const void *buf, size_t buflen);
|
||||
|
||||
/* Designated Revoker routines */
|
||||
const unsigned char* cdk_key_desig_revoker_walk (cdk_desig_revoker_t root,
|
||||
cdk_desig_revoker_t * ctx,
|
||||
int *r_class, int *r_algid);
|
||||
|
||||
#define is_RSA(a) ((a) == CDK_PK_RSA \
|
||||
|| (a) == CDK_PK_RSA_E \
|
||||
|| (a) == CDK_PK_RSA_S)
|
||||
#define is_ELG(a) ((a) == CDK_PK_ELG_E)
|
||||
#define is_DSA(a) ((a) == CDK_PK_DSA)
|
||||
|
||||
/* Encrypt the given session key @SK with the public key @PK
|
||||
and write the contents into the packet @PKE. */
|
||||
cdk_error_t cdk_pk_encrypt (cdk_pubkey_t pk, cdk_pkt_pubkey_enc_t pke,
|
||||
gcry_mpi_t sk);
|
||||
|
||||
/* Decrypt the given encrypted session key in @PKE with the secret key
|
||||
@SK and store it in @R_SK. */
|
||||
cdk_error_t cdk_pk_decrypt (cdk_seckey_t sk, cdk_pkt_pubkey_enc_t pke,
|
||||
gcry_mpi_t *r_sk);
|
||||
|
||||
/* Sign the given message digest @MD with the secret key @SK and
|
||||
store the signature in the packet @SIG. */
|
||||
cdk_error_t cdk_pk_sign (cdk_seckey_t sk, cdk_pkt_signature_t sig,
|
||||
const unsigned char *md);
|
||||
|
||||
/* Verify the given signature in @SIG with the public key @PK
|
||||
and compare it against the message digest @MD. */
|
||||
cdk_error_t cdk_pk_verify (cdk_pubkey_t pk, cdk_pkt_signature_t sig,
|
||||
const unsigned char *md);
|
||||
|
||||
/* Use cdk_pk_get_npkey() and cdk_pk_get_nskey to find out how much
|
||||
multiprecision integers a key consists of. */
|
||||
|
||||
/* Return a multi precision integer of the public key with the index @IDX
|
||||
in the buffer @BUF. @R_NWRITTEN will contain the length in octets.
|
||||
Optional @R_NBITS may contain the size in bits. */
|
||||
cdk_error_t cdk_pk_get_mpi (cdk_pubkey_t pk, size_t idx,
|
||||
unsigned char *buf, size_t buflen,
|
||||
size_t *r_nwritten, size_t *r_nbits);
|
||||
|
||||
/* Same as the function above but of the secret key. */
|
||||
cdk_error_t cdk_sk_get_mpi (cdk_seckey_t sk, size_t idx,
|
||||
unsigned char * buf, size_t buflen,
|
||||
size_t *r_nwritten, size_t *r_nbits);
|
||||
|
||||
/* Helper to get the exact number of multi precision integers
|
||||
for the given object. */
|
||||
int cdk_pk_get_nbits (cdk_pubkey_t pk);
|
||||
int cdk_pk_get_npkey (int algo);
|
||||
int cdk_pk_get_nskey (int algo);
|
||||
int cdk_pk_get_nsig (int algo);
|
||||
int cdk_pk_get_nenc (int algo);
|
||||
|
||||
/* Fingerprint and key ID routines. */
|
||||
|
||||
/* Calculate the fingerprint of the given public key.
|
||||
the FPR parameter must be at least 20 octets to hold the SHA1 hash. */
|
||||
cdk_error_t cdk_pk_get_fingerprint (cdk_pubkey_t pk, unsigned char *fpr);
|
||||
|
||||
/* Same as above, but with additional sanity checks of the buffer size. */
|
||||
cdk_error_t cdk_pk_to_fingerprint (cdk_pubkey_t pk,
|
||||
unsigned char *fpr, size_t fprlen,
|
||||
size_t *r_nout);
|
||||
|
||||
/* Derive the keyid from the fingerprint. This is only possible for
|
||||
modern, version 4 keys. */
|
||||
unsigned int cdk_pk_fingerprint_get_keyid (const unsigned char *fpr,
|
||||
size_t fprlen,
|
||||
unsigned int *keyid);
|
||||
|
||||
/* Various functions to get the keyid from the specific packet type. */
|
||||
unsigned int cdk_pk_get_keyid (cdk_pubkey_t pk, unsigned int *keyid);
|
||||
unsigned int cdk_sk_get_keyid (cdk_seckey_t sk, unsigned int *keyid);
|
||||
unsigned int cdk_sig_get_keyid (cdk_pkt_signature_t sig,
|
||||
unsigned int *keyid);
|
||||
|
||||
/* Key release functions. */
|
||||
void cdk_pk_release (cdk_pubkey_t pk);
|
||||
void cdk_sk_release (cdk_seckey_t sk);
|
||||
|
||||
/* Secret key related functions. */
|
||||
cdk_error_t cdk_sk_unprotect (cdk_seckey_t sk, const char *pw);
|
||||
cdk_error_t cdk_sk_protect (cdk_seckey_t sk, const char *pw);
|
||||
|
||||
/* Create a public key with the data from the secret key @SK. */
|
||||
cdk_error_t cdk_pk_from_secret_key (cdk_seckey_t sk,
|
||||
cdk_pubkey_t *ret_pk);
|
||||
|
||||
/* Sexp conversion of keys. */
|
||||
cdk_error_t cdk_pubkey_to_sexp (cdk_pubkey_t pk,
|
||||
char **sexp, size_t *len);
|
||||
cdk_error_t cdk_seckey_to_sexp (cdk_seckey_t sk,
|
||||
char **sexp, size_t *len);
|
||||
|
||||
|
||||
/* Data Encryption Key (DEK) routines */
|
||||
cdk_error_t cdk_dek_new (cdk_dek_t *r_dek);
|
||||
void cdk_dek_free (cdk_dek_t dek);
|
||||
|
||||
/* Set the symmetric cipher algorithm which shall be used for this
|
||||
DEK object. */
|
||||
cdk_error_t cdk_dek_set_cipher (cdk_dek_t dek, int cipher_algo);
|
||||
|
||||
/* Return the symmetric cipher which is used for this DEK object. */
|
||||
cdk_error_t cdk_dek_get_cipher (cdk_dek_t dek, int *r_cipher_algo);
|
||||
|
||||
|
||||
/* Set the session key for the given DEK object.
|
||||
If @KEY and @KEYLEN are both NULL/0, a random key will be generated
|
||||
and stored in the DEK object. */
|
||||
cdk_error_t cdk_dek_set_key (cdk_dek_t dek, const unsigned char *key,
|
||||
size_t keylen);
|
||||
|
||||
/* Enable the MDC feature for the current DEK object. */
|
||||
void cdk_dek_set_mdc_flag (cdk_dek_t dek, int val);
|
||||
|
||||
/* Return if the MDC feature is enabled for the current DEK object.*/
|
||||
int cdk_dek_get_mdc_flag (cdk_dek_t dek);
|
||||
|
||||
/* Transform the given passphrase into a DEK.
|
||||
If @rndsalt is set, a random salt will be generated. */
|
||||
cdk_error_t cdk_dek_from_passphrase (cdk_dek_t *ret_dek, int cipher_algo,
|
||||
cdk_s2k_t s2k, int rndsalt,
|
||||
const char *passphrase);
|
||||
|
||||
/* String to Key routines. */
|
||||
cdk_error_t cdk_s2k_new (cdk_s2k_t *ret_s2k, int mode, int digest_algo,
|
||||
const unsigned char *salt);
|
||||
void cdk_s2k_free (cdk_s2k_t s2k);
|
||||
|
||||
cdk_error_t cdk_file_armor (cdk_ctx_t hd, const char *file,
|
||||
const char *output);
|
||||
cdk_error_t cdk_file_dearmor (const char * file, const char *output);
|
||||
int cdk_armor_filter_use (cdk_stream_t inp);
|
||||
|
||||
/* Protect the inbuf with ASCII armor of the specified type.
|
||||
If @outbuf and @outlen are NULL, the function returns the calculated
|
||||
size of the base64 encoded data in @nwritten. */
|
||||
cdk_error_t cdk_armor_encode_buffer (const unsigned char *inbuf, size_t inlen,
|
||||
char *outbuf, size_t outlen,
|
||||
size_t *nwritten, int type);
|
||||
|
||||
|
||||
/* This context contain user callbacks for different stream operations.
|
||||
Some of these callbacks might be NULL to indicate that the callback
|
||||
is not used. */
|
||||
struct cdk_stream_cbs_s
|
||||
{
|
||||
cdk_error_t (*open)(void *);
|
||||
cdk_error_t (*release)(void *);
|
||||
int (*read)(void *, void *buf, size_t);
|
||||
int (*write)(void *, const void *buf, size_t);
|
||||
int (*seek)(void *, off_t);
|
||||
};
|
||||
typedef struct cdk_stream_cbs_s *cdk_stream_cbs_t;
|
||||
|
||||
|
||||
int cdk_stream_is_compressed (cdk_stream_t s);
|
||||
|
||||
/* Return a stream object which is associated to a socket. */
|
||||
cdk_error_t cdk_stream_sockopen (const char *host, unsigned short port,
|
||||
cdk_stream_t *ret_out);
|
||||
|
||||
/* Return a stream object which is associated to an existing file. */
|
||||
cdk_error_t cdk_stream_open (const char * file, cdk_stream_t * ret_s);
|
||||
|
||||
/* Return a stream object which is associated to a file which will
|
||||
be created when the stream is closed. */
|
||||
cdk_error_t cdk_stream_new (const char * file, cdk_stream_t * ret_s);
|
||||
|
||||
/* Return a stream object with custom callback functions for the
|
||||
various core operations. */
|
||||
cdk_error_t cdk_stream_new_from_cbs (cdk_stream_cbs_t cbs, void *opa,
|
||||
cdk_stream_t *ret_s);
|
||||
cdk_error_t cdk_stream_create (const char * file, cdk_stream_t * ret_s);
|
||||
cdk_error_t cdk_stream_tmp_new (cdk_stream_t *r_out);
|
||||
cdk_error_t cdk_stream_tmp_from_mem (const void * buf, size_t buflen,
|
||||
cdk_stream_t *r_out);
|
||||
void cdk_stream_tmp_set_mode (cdk_stream_t s, int val);
|
||||
cdk_error_t cdk_stream_flush (cdk_stream_t s);
|
||||
cdk_error_t cdk_stream_enable_cache (cdk_stream_t s, int val);
|
||||
cdk_error_t cdk_stream_filter_disable (cdk_stream_t s, int type);
|
||||
cdk_error_t cdk_stream_close (cdk_stream_t s);
|
||||
off_t cdk_stream_get_length (cdk_stream_t s);
|
||||
int cdk_stream_read (cdk_stream_t s, void * buf, size_t count);
|
||||
int cdk_stream_write (cdk_stream_t s, const void * buf, size_t count);
|
||||
int cdk_stream_putc (cdk_stream_t s, int c);
|
||||
int cdk_stream_getc (cdk_stream_t s);
|
||||
int cdk_stream_eof (cdk_stream_t s);
|
||||
off_t cdk_stream_tell (cdk_stream_t s);
|
||||
cdk_error_t cdk_stream_seek (cdk_stream_t s, off_t offset);
|
||||
cdk_error_t cdk_stream_set_armor_flag (cdk_stream_t s, int type);
|
||||
|
||||
/* Push the literal filter for the given stream. */
|
||||
cdk_error_t cdk_stream_set_literal_flag (cdk_stream_t s,
|
||||
cdk_lit_format_t mode,
|
||||
const char * fname);
|
||||
|
||||
cdk_error_t cdk_stream_set_cipher_flag (cdk_stream_t s, cdk_dek_t dek,
|
||||
int use_mdc);
|
||||
cdk_error_t cdk_stream_set_compress_flag (cdk_stream_t s, int algo, int level);
|
||||
cdk_error_t cdk_stream_set_hash_flag (cdk_stream_t s, int algo);
|
||||
cdk_error_t cdk_stream_set_text_flag (cdk_stream_t s, const char * lf);
|
||||
cdk_error_t cdk_stream_kick_off (cdk_stream_t inp, cdk_stream_t out);
|
||||
cdk_error_t cdk_stream_mmap (cdk_stream_t s, unsigned char **ret_buf,
|
||||
size_t *ret_buflen);
|
||||
cdk_error_t cdk_stream_mmap_part (cdk_stream_t s, off_t off, size_t len,
|
||||
unsigned char **ret_buf, size_t *ret_buflen);
|
||||
|
||||
/* Read from the stream but restore the file pointer after reading
|
||||
the requested amount of bytes. */
|
||||
int cdk_stream_peek (cdk_stream_t inp, unsigned char *buf, size_t buflen);
|
||||
|
||||
/* A wrapper around the various new_from_XXX functions. Because
|
||||
the function does not support all combinations, the dedicated
|
||||
functions should be preferred. */
|
||||
cdk_error_t cdk_keydb_new (cdk_keydb_hd_t *r_hd, int type, void *data,
|
||||
size_t count);
|
||||
|
||||
/* Create a new key db handle from a memory buffer. */
|
||||
cdk_error_t cdk_keydb_new_from_mem (cdk_keydb_hd_t *r_hd, int secret,
|
||||
const void *data, size_t datlen);
|
||||
|
||||
/* Create a new key db which uses an existing file. */
|
||||
cdk_error_t cdk_keydb_new_from_file (cdk_keydb_hd_t *r_hd, int secret,
|
||||
const char *fname);
|
||||
|
||||
/* Uses a stream as the key db input. For searching it is important
|
||||
that the seek function is supported on the stream. Furthermore,
|
||||
the stream is not closed in cdk_keydb_free(). The caller must do it. */
|
||||
cdk_error_t cdk_keydb_new_from_stream (cdk_keydb_hd_t *r_hd, int secret,
|
||||
cdk_stream_t in);
|
||||
|
||||
/* Check that a secret key with the given key ID is available. */
|
||||
cdk_error_t cdk_keydb_check_sk (cdk_keydb_hd_t hd, unsigned int *keyid);
|
||||
|
||||
/* Prepare the key db search. */
|
||||
cdk_error_t cdk_keydb_search_start (cdk_keydb_hd_t hd, int type, void *desc);
|
||||
|
||||
/* Return a key which matches a valid description given in
|
||||
cdk_keydb_search_start(). */
|
||||
cdk_error_t cdk_keydb_search (cdk_keydb_hd_t hd, cdk_kbnode_t *ret_key);
|
||||
|
||||
/* Release the key db handle and all its resources. */
|
||||
void cdk_keydb_free (cdk_keydb_hd_t hd);
|
||||
|
||||
/* The following functions will try to find a key in the given key
|
||||
db handle either by keyid, by fingerprint or by some pattern. */
|
||||
cdk_error_t cdk_keydb_get_bykeyid (cdk_keydb_hd_t hd, unsigned int *keyid,
|
||||
cdk_kbnode_t *ret_pk);
|
||||
cdk_error_t cdk_keydb_get_byfpr (cdk_keydb_hd_t hd, const unsigned char *fpr,
|
||||
cdk_kbnode_t *ret_pk);
|
||||
cdk_error_t cdk_keydb_get_bypattern (cdk_keydb_hd_t hd, const char *patt,
|
||||
cdk_kbnode_t *ret_pk);
|
||||
|
||||
/* These function, in contrast to most other key db functions, only
|
||||
return the public or secret key packet without the additional
|
||||
signatures and user IDs. */
|
||||
cdk_error_t cdk_keydb_get_pk (cdk_keydb_hd_t khd, unsigned int * keyid,
|
||||
cdk_pubkey_t *ret_pk);
|
||||
cdk_error_t cdk_keydb_get_sk (cdk_keydb_hd_t khd, unsigned int * keyid,
|
||||
cdk_seckey_t *ret_sk);
|
||||
|
||||
/* Try to read the next key block from the given input stream.
|
||||
The key will be returned in @RET_KEY on success. */
|
||||
cdk_error_t cdk_keydb_get_keyblock (cdk_stream_t inp, cdk_kbnode_t * ret_key);
|
||||
|
||||
/* Rebuild the key db index if possible. */
|
||||
cdk_error_t cdk_keydb_idx_rebuild (cdk_keydb_hd_t hd);
|
||||
|
||||
/* Export one or more keys from the given key db handle into
|
||||
the stream @OUT. The export is done by substring search and
|
||||
uses the string list @REMUSR for the pattern. */
|
||||
cdk_error_t cdk_keydb_export (cdk_keydb_hd_t hd, cdk_stream_t out,
|
||||
cdk_strlist_t remusr);
|
||||
|
||||
/* Import the given key node @knode into the key db. */
|
||||
cdk_error_t cdk_keydb_import (cdk_keydb_hd_t hd, cdk_kbnode_t knode);
|
||||
|
||||
|
||||
/* List or enumerate keys from a given key db handle. */
|
||||
|
||||
/* Start the key list process. Either use @PATT for a pattern search
|
||||
or @FPATT for a list of pattern. */
|
||||
cdk_error_t cdk_listkey_start (cdk_listkey_t *r_ctx, cdk_keydb_hd_t db,
|
||||
const char *patt, cdk_strlist_t fpatt);
|
||||
void cdk_listkey_close (cdk_listkey_t ctx);
|
||||
|
||||
/* Return the next key which matches the pattern. */
|
||||
cdk_error_t cdk_listkey_next (cdk_listkey_t ctx, cdk_kbnode_t *ret_key);
|
||||
|
||||
cdk_kbnode_t cdk_kbnode_new (cdk_packet_t pkt);
|
||||
cdk_error_t cdk_kbnode_read_from_mem (cdk_kbnode_t * ret_node,
|
||||
const unsigned char * buf,
|
||||
size_t buflen);
|
||||
cdk_error_t cdk_kbnode_write_to_mem (cdk_kbnode_t node,
|
||||
unsigned char * buf, size_t * r_nbytes);
|
||||
cdk_error_t cdk_kbnode_write_to_mem_alloc (cdk_kbnode_t node,
|
||||
unsigned char **r_buf,
|
||||
size_t *r_buflen);
|
||||
|
||||
void cdk_kbnode_release (cdk_kbnode_t node);
|
||||
cdk_kbnode_t cdk_kbnode_walk (cdk_kbnode_t root, cdk_kbnode_t * ctx, int all);
|
||||
cdk_packet_t cdk_kbnode_find_packet (cdk_kbnode_t node, int pkttype);
|
||||
cdk_packet_t cdk_kbnode_get_packet (cdk_kbnode_t node);
|
||||
cdk_kbnode_t cdk_kbnode_find (cdk_kbnode_t node, int pkttype);
|
||||
cdk_kbnode_t cdk_kbnode_find_prev (cdk_kbnode_t root, cdk_kbnode_t node,
|
||||
int pkttype);
|
||||
cdk_kbnode_t cdk_kbnode_find_next (cdk_kbnode_t node, int pkttype);
|
||||
cdk_error_t cdk_kbnode_hash (cdk_kbnode_t node, gcry_md_hd_t md, int is_v4,
|
||||
int pkttype, int flags);
|
||||
|
||||
/* Check each signature in the key node and return a summary of the
|
||||
key status in @r_status. Values of cdk_key_flag_t are used. */
|
||||
cdk_error_t cdk_pk_check_sigs (cdk_kbnode_t knode, cdk_keydb_hd_t hd,
|
||||
int *r_status);
|
||||
|
||||
/* Check the self signature of the key to make sure it is valid. */
|
||||
cdk_error_t cdk_pk_check_self_sig (cdk_kbnode_t knode, int *r_status);
|
||||
|
||||
/* Return a matching algorithm from the given public key list.
|
||||
@PREFTYPE can be either sym-cipher/compress/digest. */
|
||||
int cdk_pklist_select_algo (cdk_keylist_t pkl, int preftype);
|
||||
|
||||
/* Return 0 or 1 if the given key list is able to understand the
|
||||
MDC feature. */
|
||||
int cdk_pklist_use_mdc (cdk_keylist_t pkl);
|
||||
cdk_error_t cdk_pklist_build (cdk_keylist_t *ret_pkl, cdk_keydb_hd_t hd,
|
||||
cdk_strlist_t remusr, int use);
|
||||
void cdk_pklist_release (cdk_keylist_t pkl);
|
||||
|
||||
/* Encrypt the given DEK key with the list of public keys given
|
||||
in @PKL. The result will be written to the stream output @OUT. */
|
||||
cdk_error_t cdk_pklist_encrypt (cdk_keylist_t pkl, cdk_dek_t dek,
|
||||
cdk_stream_t out);
|
||||
|
||||
/* Secret key lists */
|
||||
cdk_error_t cdk_sklist_build (cdk_keylist_t * ret_skl,
|
||||
cdk_keydb_hd_t db, cdk_ctx_t hd,
|
||||
cdk_strlist_t locusr,
|
||||
int unlock, unsigned int use);
|
||||
void cdk_sklist_release (cdk_keylist_t skl);
|
||||
cdk_error_t cdk_sklist_write (cdk_keylist_t skl, cdk_stream_t outp,
|
||||
gcry_md_hd_t mdctx,
|
||||
int sigclass, int sigver);
|
||||
cdk_error_t cdk_sklist_write_onepass (cdk_keylist_t skl, cdk_stream_t outp,
|
||||
int sigclass, int mdalgo);
|
||||
|
||||
/* Encrypt the given stream @INP with the recipients given in @REMUSR.
|
||||
If @REMUSR is NULL, symmetric encryption will be used. The output
|
||||
will be written to @OUT. */
|
||||
cdk_error_t cdk_stream_encrypt (cdk_ctx_t hd, cdk_strlist_t remusr,
|
||||
cdk_stream_t inp, cdk_stream_t out);
|
||||
|
||||
/* Decrypt the @INP stream into @OUT. */
|
||||
cdk_error_t cdk_stream_decrypt (cdk_ctx_t hd, cdk_stream_t inp,
|
||||
cdk_stream_t out);
|
||||
|
||||
/* Same as the function above but it works on files. */
|
||||
cdk_error_t cdk_file_encrypt (cdk_ctx_t hd, cdk_strlist_t remusr,
|
||||
const char *file, const char *output);
|
||||
cdk_error_t cdk_file_decrypt (cdk_ctx_t hd, const char * file,
|
||||
const char *output);
|
||||
|
||||
/* Generic function to transform data. The mode can be either sign,
|
||||
verify, encrypt, decrypt, import or export. The meanings of the
|
||||
parameters are similar to the functions above.
|
||||
@OUTBUF will contain the output and @OUTSIZE the length of it. */
|
||||
cdk_error_t cdk_data_transform (cdk_ctx_t hd, enum cdk_crypto_mode_t mode,
|
||||
cdk_strlist_t locusr, cdk_strlist_t remusr,
|
||||
const void * inbuf, size_t insize,
|
||||
unsigned char ** outbuf, size_t * outsize,
|
||||
int modval);
|
||||
|
||||
/* Sign the stream @INP. Optionally, the output will be encrypted
|
||||
if @REMUSR is not NULL and the @ENCRYPTFLAG is set.
|
||||
The output will be written to @OUT.
|
||||
@LOCUSR contains one ore more pattern for the secret key(s) to use. */
|
||||
cdk_error_t cdk_stream_sign (cdk_ctx_t hd, cdk_stream_t inp, cdk_stream_t out,
|
||||
cdk_strlist_t locusr, cdk_strlist_t remusr,
|
||||
int encryptflag, int sigmode);
|
||||
|
||||
/* Same as the function above but it works on files. */
|
||||
cdk_error_t cdk_file_sign (cdk_ctx_t hd, cdk_strlist_t locusr,
|
||||
cdk_strlist_t remusr,
|
||||
const char *file, const char *output,
|
||||
int sigmode, int encryptflag);
|
||||
|
||||
cdk_error_t cdk_stream_verify (cdk_ctx_t hd, cdk_stream_t inp,
|
||||
cdk_stream_t data,
|
||||
cdk_stream_t out);
|
||||
|
||||
/* Verify the given file @FILE. For a detached signature, @DATA_FILE
|
||||
contains the actual file data and @FILE is only the signature.
|
||||
If the @OUTPUT is not NULL, the plaintext will be written to this file. */
|
||||
cdk_error_t cdk_file_verify (cdk_ctx_t hd, const char *file,
|
||||
const char *data_file, const char *output);
|
||||
|
||||
int cdk_trustdb_get_validity (cdk_stream_t inp, cdk_pkt_userid_t id,
|
||||
int *r_val);
|
||||
int cdk_trustdb_get_ownertrust (cdk_stream_t inp, cdk_pubkey_t pk,
|
||||
int *r_val, int *r_flags);
|
||||
|
||||
void cdk_strlist_free (cdk_strlist_t sl);
|
||||
cdk_strlist_t cdk_strlist_add (cdk_strlist_t * list, const char * string);
|
||||
cdk_strlist_t cdk_strlist_next (cdk_strlist_t root, const char **r_str);
|
||||
const char * cdk_check_version (const char * req_version);
|
||||
/* UTF8 */
|
||||
char* cdk_utf8_encode (const char * string);
|
||||
char* cdk_utf8_decode (const char * string, size_t length, int delim);
|
||||
|
||||
/* Try to receive the key, which has the key ID @KEYID, from the
|
||||
keyserver host @HOST and the port @PORT. */
|
||||
cdk_error_t cdk_keyserver_recv_key (const char *host, int port,
|
||||
const unsigned char *keyid, int kid_type,
|
||||
cdk_kbnode_t *r_key);
|
||||
|
||||
cdk_error_t cdk_keygen_new (cdk_keygen_ctx_t * r_hd);
|
||||
void cdk_keygen_free (cdk_keygen_ctx_t hd);
|
||||
|
||||
/* Set the preferences of the given type for the new key.
|
||||
@ARRAY is an array which list of algorithm IDs. */
|
||||
cdk_error_t cdk_keygen_set_prefs (cdk_keygen_ctx_t hd,
|
||||
enum cdk_pref_type_t type,
|
||||
const unsigned char * array, size_t n);
|
||||
cdk_error_t cdk_keygen_set_algo_info (cdk_keygen_ctx_t hd, int type,
|
||||
int usage, enum cdk_pubkey_algo_t algo,
|
||||
unsigned int bits);
|
||||
int cdk_keygen_set_keyserver_flags (cdk_keygen_ctx_t hd, int no_modify,
|
||||
const char *pref_url);
|
||||
int cdk_keygen_set_expire_date (cdk_keygen_ctx_t hd, int type,
|
||||
long timestamp);
|
||||
|
||||
/* Set the user ID specifc parts for the new key.
|
||||
It is suggested to use a name in the form of
|
||||
'First Name' 'Last Name' <email-address@host.domain> */
|
||||
void cdk_keygen_set_name (cdk_keygen_ctx_t hd, const char * name);
|
||||
void cdk_keygen_set_passphrase (cdk_keygen_ctx_t hd, const char * pass);
|
||||
cdk_error_t cdk_keygen_start (cdk_keygen_ctx_t hd);
|
||||
cdk_error_t cdk_keygen_save (cdk_keygen_ctx_t hd,
|
||||
const char * pubf,
|
||||
const char * secf);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OPENCDK_H */
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/* packet.h
|
||||
* Copyright (C) 2002, 2003, 2007 Timo Schulz
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifndef CDK_PACKET_H
|
||||
#define CDK_PACKET_H
|
||||
|
||||
struct cdk_kbnode_s
|
||||
{
|
||||
struct cdk_kbnode_s *next;
|
||||
cdk_packet_t pkt;
|
||||
unsigned int is_deleted:1;
|
||||
unsigned int is_cloned:1;
|
||||
};
|
||||
|
||||
/*-- new-packet.c --*/
|
||||
void _cdk_free_mpibuf (size_t n, gcry_mpi_t *array);
|
||||
void _cdk_free_userid (cdk_pkt_userid_t uid);
|
||||
void _cdk_free_signature( cdk_pkt_signature_t sig );
|
||||
cdk_prefitem_t _cdk_copy_prefs( const cdk_prefitem_t prefs );
|
||||
int _cdk_copy_userid( cdk_pkt_userid_t *dst, cdk_pkt_userid_t src );
|
||||
int _cdk_copy_pubkey( cdk_pkt_pubkey_t* dst, cdk_pkt_pubkey_t src );
|
||||
int _cdk_copy_seckey( cdk_pkt_seckey_t* dst, cdk_pkt_seckey_t src );
|
||||
int _cdk_copy_pk_to_sk( cdk_pkt_pubkey_t pk, cdk_pkt_seckey_t sk );
|
||||
int _cdk_copy_signature( cdk_pkt_signature_t* dst, cdk_pkt_signature_t src );
|
||||
int _cdk_pubkey_compare( cdk_pkt_pubkey_t a, cdk_pkt_pubkey_t b );
|
||||
|
||||
#endif /* CDK_PACKET_H */
|
||||
|
||||
@@ -1,1380 +0,0 @@
|
||||
/* pubkey.c - Public key API
|
||||
* Copyright (C) 2007 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2002, 2003, 2007 Timo Schulz
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <gcrypt.h>
|
||||
|
||||
#include "opencdk.h"
|
||||
#include "main.h"
|
||||
#include "packet.h"
|
||||
|
||||
|
||||
/* Convert the given secret key into a gcrypt SEXP object. */
|
||||
static int
|
||||
seckey_to_sexp (gcry_sexp_t * r_skey, cdk_seckey_t sk)
|
||||
{
|
||||
gcry_sexp_t sexp = NULL;
|
||||
gcry_mpi_t *mpk = NULL, *msk = NULL;
|
||||
gcry_error_t err;
|
||||
cdk_pubkey_t pk;
|
||||
const char *fmt;
|
||||
|
||||
if (!r_skey || !sk || !sk->pk)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
pk = sk->pk;
|
||||
mpk = pk->mpi;
|
||||
msk = sk->mpi;
|
||||
|
||||
*r_skey = NULL;
|
||||
if (is_RSA (sk->pubkey_algo))
|
||||
{
|
||||
fmt = "(private-key(openpgp-rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))";
|
||||
err = gcry_sexp_build (&sexp, NULL, fmt, mpk[0], mpk[1],
|
||||
msk[0], msk[1], msk[2], msk[3]);
|
||||
}
|
||||
else if (is_ELG (sk->pubkey_algo))
|
||||
{
|
||||
fmt = "(private-key(openpgp-elg(p%m)(g%m)(y%m)(x%m)))";
|
||||
err = gcry_sexp_build (&sexp, NULL, fmt, mpk[0], mpk[1],
|
||||
mpk[2], msk[0]);
|
||||
}
|
||||
else if (is_DSA (sk->pubkey_algo))
|
||||
{
|
||||
fmt = "(private-key(openpgp-dsa(p%m)(q%m)(g%m)(y%m)(x%m)))";
|
||||
err = gcry_sexp_build (&sexp, NULL, fmt, mpk[0], mpk[1], mpk[2],
|
||||
mpk[3], msk[0]);
|
||||
}
|
||||
else
|
||||
return CDK_Inv_Algo;
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
*r_skey = sexp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Convert the given public key to a gcrypt SEXP object. */
|
||||
static cdk_error_t
|
||||
pubkey_to_sexp (gcry_sexp_t * r_key_sexp, cdk_pubkey_t pk)
|
||||
{
|
||||
gcry_mpi_t *m;
|
||||
gcry_error_t err;
|
||||
const char *fmt = NULL;
|
||||
cdk_error_t rc = 0;
|
||||
|
||||
if (!r_key_sexp || !pk)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
m = pk->mpi;
|
||||
if (is_RSA (pk->pubkey_algo))
|
||||
{
|
||||
fmt = "(public-key(openpgp-rsa(n%m)(e%m)))";
|
||||
err = gcry_sexp_build (r_key_sexp, NULL, fmt, m[0], m[1]);
|
||||
if (err)
|
||||
rc = map_gcry_error (err);
|
||||
}
|
||||
else if (is_ELG (pk->pubkey_algo))
|
||||
{
|
||||
fmt = "(public-key(openpgp-elg(p%m)(g%m)(y%m)))";
|
||||
err = gcry_sexp_build (r_key_sexp, NULL, fmt, m[0], m[1], m[2]);
|
||||
if (err)
|
||||
rc = map_gcry_error (err);
|
||||
}
|
||||
else if (is_DSA (pk->pubkey_algo))
|
||||
{
|
||||
fmt = "(public-key(openpgp-dsa(p%m)(q%m)(g%m)(y%m)))";
|
||||
err = gcry_sexp_build (r_key_sexp, NULL, fmt, m[0], m[1], m[2], m[3]);
|
||||
if (err)
|
||||
rc = map_gcry_error (err);
|
||||
}
|
||||
else
|
||||
rc = CDK_Inv_Algo;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
enckey_to_sexp (gcry_sexp_t * r_sexp, gcry_mpi_t esk)
|
||||
{
|
||||
gcry_error_t err;
|
||||
|
||||
if (!r_sexp || !esk)
|
||||
return CDK_Inv_Value;
|
||||
err = gcry_sexp_build (r_sexp, NULL, "%m", esk);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
digest_to_sexp (gcry_sexp_t * r_md_sexp, int digest_algo,
|
||||
const byte * md, size_t mdlen)
|
||||
{
|
||||
gcry_mpi_t m;
|
||||
gcry_error_t err;
|
||||
|
||||
if (!r_md_sexp || !md)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (!mdlen)
|
||||
mdlen = gcry_md_get_algo_dlen (digest_algo);
|
||||
if (!mdlen)
|
||||
return CDK_Inv_Algo;
|
||||
|
||||
err = gcry_mpi_scan (&m, GCRYMPI_FMT_USG, md, mdlen, &mdlen);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
|
||||
err = gcry_sexp_build (r_md_sexp, NULL, "%m", m);
|
||||
gcry_mpi_release (m);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
sexp_to_mpi (gcry_sexp_t sexp, const char *val, gcry_mpi_t * ret_buf)
|
||||
{
|
||||
gcry_sexp_t list;
|
||||
|
||||
if (!sexp || !val || !ret_buf)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
list = gcry_sexp_find_token (sexp, val, 0);
|
||||
if (!list)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
*ret_buf = gcry_sexp_nth_mpi (list, 1, 0);
|
||||
gcry_sexp_release (list);
|
||||
if (!*ret_buf)
|
||||
return CDK_Inv_Value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
sexp_to_sig (cdk_pkt_signature_t sig, gcry_sexp_t sexp)
|
||||
{
|
||||
if (!sig || !sexp)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
/* ElGamal signatures are not supported any longer. */
|
||||
if (is_ELG (sig->pubkey_algo))
|
||||
{
|
||||
_cdk_log_debug ("sexp_to_sig: unsupported signature type (ElGamal)\n");
|
||||
return CDK_Not_Implemented;
|
||||
}
|
||||
|
||||
if (is_RSA (sig->pubkey_algo))
|
||||
return sexp_to_mpi (sexp, "s", &sig->mpi[0]);
|
||||
else if (is_DSA (sig->pubkey_algo))
|
||||
{
|
||||
cdk_error_t rc;
|
||||
|
||||
rc = sexp_to_mpi (sexp, "r", &sig->mpi[0]);
|
||||
if (!rc)
|
||||
rc = sexp_to_mpi (sexp, "s", &sig->mpi[1]);
|
||||
return rc;
|
||||
}
|
||||
return CDK_Inv_Algo;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
sig_to_sexp (gcry_sexp_t * r_sig_sexp, cdk_pkt_signature_t sig)
|
||||
{
|
||||
gcry_error_t err;
|
||||
cdk_error_t rc;
|
||||
const char *fmt;
|
||||
|
||||
if (!r_sig_sexp || !sig)
|
||||
return CDK_Inv_Value;
|
||||
if (is_ELG (sig->pubkey_algo))
|
||||
return CDK_Not_Implemented;
|
||||
|
||||
rc = 0;
|
||||
if (is_RSA (sig->pubkey_algo))
|
||||
{
|
||||
fmt = "(sig-val(openpgp-rsa(s%m)))";
|
||||
err = gcry_sexp_build (r_sig_sexp, NULL, fmt, sig->mpi[0]);
|
||||
if (err)
|
||||
rc = map_gcry_error (err);
|
||||
}
|
||||
else if (is_DSA (sig->pubkey_algo))
|
||||
{
|
||||
fmt = "(sig-val(openpgp-dsa(r%m)(s%m)))";
|
||||
err = gcry_sexp_build (r_sig_sexp, NULL, fmt, sig->mpi[0], sig->mpi[1]);
|
||||
if (err)
|
||||
rc = map_gcry_error (err);
|
||||
}
|
||||
else
|
||||
rc = CDK_Inv_Algo;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
sexp_to_pubenc (cdk_pkt_pubkey_enc_t enc, gcry_sexp_t sexp)
|
||||
{
|
||||
if (!sexp || !enc)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (is_RSA (enc->pubkey_algo))
|
||||
return sexp_to_mpi (sexp, "a", &enc->mpi[0]);
|
||||
else if (is_ELG (enc->pubkey_algo))
|
||||
{
|
||||
cdk_error_t rc = sexp_to_mpi (sexp, "a", &enc->mpi[0]);
|
||||
if (!rc)
|
||||
rc = sexp_to_mpi (sexp, "b", &enc->mpi[1]);
|
||||
return rc;
|
||||
}
|
||||
return CDK_Inv_Algo;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
pubenc_to_sexp (gcry_sexp_t * r_sexp, cdk_pkt_pubkey_enc_t enc)
|
||||
{
|
||||
gcry_sexp_t sexp = NULL;
|
||||
gcry_error_t err;
|
||||
const char *fmt;
|
||||
|
||||
if (!r_sexp || !enc)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
*r_sexp = NULL;
|
||||
if (is_RSA (enc->pubkey_algo))
|
||||
{
|
||||
fmt = "(enc-val(openpgp-rsa((a%m))))";
|
||||
err = gcry_sexp_build (&sexp, NULL, fmt, enc->mpi[0]);
|
||||
}
|
||||
else if (is_ELG (enc->pubkey_algo))
|
||||
{
|
||||
fmt = "(enc-val(openpgp-elg((a%m)(b%m))))";
|
||||
err = gcry_sexp_build (&sexp, NULL, fmt, enc->mpi[0], enc->mpi[1]);
|
||||
}
|
||||
else
|
||||
return CDK_Inv_Algo;
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
*r_sexp = sexp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
is_unprotected (cdk_seckey_t sk)
|
||||
{
|
||||
if (sk->is_protected && !sk->mpi[0])
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pk_encrypt:
|
||||
* @pk: the public key
|
||||
* @pke: the public key encrypted packet
|
||||
* @esk: the actual session key
|
||||
*
|
||||
* Encrypt the session key in @esk and write its encrypted content
|
||||
* into the @pke struct.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_pk_encrypt (cdk_pubkey_t pk, cdk_pkt_pubkey_enc_t pke, gcry_mpi_t esk)
|
||||
{
|
||||
gcry_sexp_t s_data = NULL, s_pkey = NULL, s_ciph = NULL;
|
||||
gcry_error_t err;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!pk || !esk || !pke)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (!KEY_CAN_ENCRYPT (pk->pubkey_algo))
|
||||
return CDK_Inv_Algo;
|
||||
|
||||
rc = enckey_to_sexp (&s_data, esk);
|
||||
if (!rc)
|
||||
rc = pubkey_to_sexp (&s_pkey, pk);
|
||||
if (!rc)
|
||||
{
|
||||
err = gcry_pk_encrypt (&s_ciph, s_data, s_pkey);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
}
|
||||
if (!rc)
|
||||
rc = sexp_to_pubenc (pke, s_ciph);
|
||||
|
||||
gcry_sexp_release (s_data);
|
||||
gcry_sexp_release (s_pkey);
|
||||
gcry_sexp_release (s_ciph);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pk_decrypt:
|
||||
* @sk: the secret key
|
||||
* @pke: public key encrypted packet
|
||||
* @r_sk: the object to store the plain session key
|
||||
*
|
||||
* Decrypt the encrypted session key from @pke into @r_sk.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_pk_decrypt (cdk_seckey_t sk, cdk_pkt_pubkey_enc_t pke, gcry_mpi_t * r_sk)
|
||||
{
|
||||
gcry_sexp_t s_data = NULL, s_skey = NULL, s_plain = NULL;
|
||||
cdk_error_t rc;
|
||||
gcry_error_t err;
|
||||
|
||||
if (!sk || !r_sk || !pke)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (!is_unprotected (sk))
|
||||
return CDK_Inv_Mode;
|
||||
|
||||
*r_sk = NULL;
|
||||
rc = seckey_to_sexp (&s_skey, sk);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = pubenc_to_sexp (&s_data, pke);
|
||||
if (rc)
|
||||
{
|
||||
gcry_sexp_release (s_skey);
|
||||
return rc;
|
||||
}
|
||||
|
||||
err = gcry_pk_decrypt (&s_plain, s_data, s_skey);
|
||||
if (err)
|
||||
rc = map_gcry_error (err);
|
||||
else
|
||||
*r_sk = gcry_sexp_nth_mpi (s_plain, 0, 0);
|
||||
|
||||
gcry_sexp_release (s_data);
|
||||
gcry_sexp_release (s_skey);
|
||||
gcry_sexp_release (s_plain);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pk_sign:
|
||||
* @sk: secret key
|
||||
* @sig: signature
|
||||
* @md: the message digest
|
||||
*
|
||||
* Sign the message digest from @md and write the result into @sig.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_pk_sign (cdk_seckey_t sk, cdk_pkt_signature_t sig, const byte * md)
|
||||
{
|
||||
gcry_sexp_t s_skey = NULL, s_sig = NULL, s_hash = NULL;
|
||||
byte *encmd = NULL;
|
||||
size_t enclen = 0;
|
||||
int nbits;
|
||||
cdk_error_t rc;
|
||||
gcry_error_t err;
|
||||
|
||||
if (!sk || !sk->pk || !sig || !md)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (!is_unprotected (sk))
|
||||
return CDK_Inv_Mode;
|
||||
|
||||
if (!KEY_CAN_SIGN (sig->pubkey_algo))
|
||||
return CDK_Inv_Algo;
|
||||
|
||||
nbits = cdk_pk_get_nbits (sk->pk);
|
||||
rc = _cdk_digest_encode_pkcs1 (&encmd, &enclen, sk->pk->pubkey_algo, md,
|
||||
sig->digest_algo, nbits);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = seckey_to_sexp (&s_skey, sk);
|
||||
if (!rc)
|
||||
rc = digest_to_sexp (&s_hash, sig->digest_algo, encmd, enclen);
|
||||
if (rc)
|
||||
{
|
||||
cdk_free (encmd);
|
||||
gcry_sexp_release (s_skey);
|
||||
return rc;
|
||||
}
|
||||
|
||||
err = gcry_pk_sign (&s_sig, s_hash, s_skey);
|
||||
if (err)
|
||||
rc = map_gcry_error (err);
|
||||
else
|
||||
{
|
||||
rc = sexp_to_sig (sig, s_sig);
|
||||
if (!rc)
|
||||
{
|
||||
sig->digest_start[0] = md[0];
|
||||
sig->digest_start[1] = md[1];
|
||||
}
|
||||
}
|
||||
|
||||
gcry_sexp_release (s_skey);
|
||||
gcry_sexp_release (s_hash);
|
||||
gcry_sexp_release (s_sig);
|
||||
cdk_free (encmd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pk_verify:
|
||||
* @pk: the public key
|
||||
* @sig: signature
|
||||
* @md: the message digest
|
||||
*
|
||||
* Verify the signature in @sig and compare it with the message digest in @md.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_pk_verify (cdk_pubkey_t pk, cdk_pkt_signature_t sig, const byte * md)
|
||||
{
|
||||
gcry_sexp_t s_pkey = NULL, s_sig = NULL, s_hash = NULL;
|
||||
byte *encmd = NULL;
|
||||
size_t enclen;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!pk || !sig || !md)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
rc = pubkey_to_sexp (&s_pkey, pk);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = sig_to_sexp (&s_sig, sig);
|
||||
if (rc)
|
||||
goto leave;
|
||||
|
||||
rc = _cdk_digest_encode_pkcs1 (&encmd, &enclen, pk->pubkey_algo, md,
|
||||
sig->digest_algo, cdk_pk_get_nbits (pk));
|
||||
if (rc)
|
||||
goto leave;
|
||||
|
||||
rc = digest_to_sexp (&s_hash, sig->digest_algo, encmd, enclen);
|
||||
if (rc)
|
||||
goto leave;
|
||||
|
||||
if (gcry_pk_verify (s_sig, s_hash, s_pkey))
|
||||
rc = CDK_Bad_Sig;
|
||||
|
||||
leave:
|
||||
gcry_sexp_release (s_sig);
|
||||
gcry_sexp_release (s_hash);
|
||||
gcry_sexp_release (s_pkey);
|
||||
cdk_free (encmd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pk_get_nbits:
|
||||
* @pk: the public key
|
||||
*
|
||||
* Return the length of the public key in bits.
|
||||
* The meaning of length is actually the size of the 'prime'
|
||||
* object in the key. For RSA keys the modulus, for ElG/DSA
|
||||
* the size of the public prime.
|
||||
**/
|
||||
int
|
||||
cdk_pk_get_nbits (cdk_pubkey_t pk)
|
||||
{
|
||||
if (!pk || !pk->mpi[0])
|
||||
return 0;
|
||||
return gcry_mpi_get_nbits (pk->mpi[0]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pk_get_npkey:
|
||||
* @algo: The public key algorithm.
|
||||
*
|
||||
* Return the number of multiprecison integer forming an public
|
||||
* key with the given algorithm.
|
||||
*/
|
||||
int
|
||||
cdk_pk_get_npkey (int algo)
|
||||
{
|
||||
size_t bytes;
|
||||
|
||||
if (algo == 16)
|
||||
algo = 20; /* FIXME: libgcrypt returns 0 for 16 */
|
||||
if (gcry_pk_algo_info (algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &bytes))
|
||||
return 0;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pk_get_nskey:
|
||||
* @algo: the public key algorithm
|
||||
*
|
||||
* Return the number of multiprecision integers forming an
|
||||
* secret key with the given algorithm.
|
||||
**/
|
||||
int
|
||||
cdk_pk_get_nskey (int algo)
|
||||
{
|
||||
size_t bytes;
|
||||
|
||||
if (gcry_pk_algo_info (algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &bytes))
|
||||
return 0;
|
||||
bytes -= cdk_pk_get_npkey (algo);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pk_get_nbits:
|
||||
* @algo: the public key algorithm
|
||||
*
|
||||
* Return the number of MPIs a signature consists of.
|
||||
**/
|
||||
int
|
||||
cdk_pk_get_nsig (int algo)
|
||||
{
|
||||
size_t bytes;
|
||||
|
||||
if (gcry_pk_algo_info (algo, GCRYCTL_GET_ALGO_NSIGN, NULL, &bytes))
|
||||
return 0;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pk_get_nenc:
|
||||
* @algo: the public key algorithm
|
||||
*
|
||||
* Return the number of MPI's the encrypted data consists of.
|
||||
**/
|
||||
int
|
||||
cdk_pk_get_nenc (int algo)
|
||||
{
|
||||
size_t bytes;
|
||||
|
||||
if (gcry_pk_algo_info (algo, GCRYCTL_GET_ALGO_NENCR, NULL, &bytes))
|
||||
return 0;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
_cdk_pk_algo_usage (int algo)
|
||||
{
|
||||
int usage;
|
||||
|
||||
/* The ElGamal sign+encrypt algorithm is not supported any longer. */
|
||||
switch (algo)
|
||||
{
|
||||
case CDK_PK_RSA:
|
||||
usage = CDK_KEY_USG_SIGN | CDK_KEY_USG_ENCR;
|
||||
break;
|
||||
case CDK_PK_RSA_E:
|
||||
usage = CDK_KEY_USG_ENCR;
|
||||
break;
|
||||
case CDK_PK_RSA_S:
|
||||
usage = CDK_KEY_USG_SIGN;
|
||||
break;
|
||||
case CDK_PK_ELG_E:
|
||||
usage = CDK_KEY_USG_ENCR;
|
||||
break;
|
||||
case CDK_PK_DSA:
|
||||
usage = CDK_KEY_USG_SIGN;
|
||||
break;
|
||||
default:
|
||||
usage = 0;
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
mpi_to_buffer (gcry_mpi_t a, byte * buf, size_t buflen,
|
||||
size_t * r_nwritten, size_t * r_nbits)
|
||||
{
|
||||
size_t nbits;
|
||||
|
||||
if (!a || !buf || !r_nwritten)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
nbits = gcry_mpi_get_nbits (a);
|
||||
if (r_nbits)
|
||||
*r_nbits = nbits;
|
||||
if ((nbits + 7) / 8 + 2 > buflen)
|
||||
return CDK_Too_Short;
|
||||
*r_nwritten = (nbits + 7) / 8 + 2;
|
||||
if (gcry_mpi_print (GCRYMPI_FMT_PGP, buf, buflen, r_nwritten, a))
|
||||
return CDK_Wrong_Format;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pk_get_mpi:
|
||||
* @pk: public key
|
||||
* @idx: index of the MPI to retrieve
|
||||
* @buf: buffer to hold the raw data
|
||||
* @r_nwritten: output how large the raw data is
|
||||
* @r_nbits: size of the MPI in bits.
|
||||
*
|
||||
* Return the MPI with the given index of the public key.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_pk_get_mpi (cdk_pubkey_t pk, size_t idx,
|
||||
byte * buf, size_t buflen, size_t * r_nwritten,
|
||||
size_t * r_nbits)
|
||||
{
|
||||
if (!pk || !r_nwritten)
|
||||
return CDK_Inv_Value;
|
||||
if (idx > cdk_pk_get_npkey (pk->pubkey_algo))
|
||||
return CDK_Inv_Value;
|
||||
return mpi_to_buffer (pk->mpi[idx], buf, buflen, r_nwritten, r_nbits);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_sk_get_mpi:
|
||||
* @sk: secret key
|
||||
* @idx: index of the MPI to retrieve
|
||||
* @buf: buffer to hold the raw data
|
||||
* @r_nwritten: output length of the raw data
|
||||
* @r_nbits: length of the MPI data in bits.
|
||||
*
|
||||
* Return the MPI of the given secret key with the
|
||||
* index @idx. It is important to check if the key
|
||||
* is protected and thus no real MPI data will be returned then.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_sk_get_mpi (cdk_pkt_seckey_t sk, size_t idx,
|
||||
byte * buf, size_t buflen, size_t * r_nwritten,
|
||||
size_t * r_nbits)
|
||||
{
|
||||
if (!sk || !r_nwritten)
|
||||
return CDK_Inv_Value;
|
||||
if (idx > cdk_pk_get_nskey (sk->pubkey_algo))
|
||||
return CDK_Inv_Value;
|
||||
return mpi_to_buffer (sk->mpi[idx], buf, buflen, r_nwritten, r_nbits);
|
||||
}
|
||||
|
||||
|
||||
static u16
|
||||
checksum_mpi (gcry_mpi_t m)
|
||||
{
|
||||
byte buf[MAX_MPI_BYTES + 2];
|
||||
size_t nread;
|
||||
int i;
|
||||
u16 chksum = 0;
|
||||
|
||||
if (!m)
|
||||
return 0;
|
||||
if (gcry_mpi_print (GCRYMPI_FMT_PGP, buf, DIM (buf), &nread, m))
|
||||
return 0;
|
||||
for (i = 0; i < nread; i++)
|
||||
chksum += buf[i];
|
||||
return chksum;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_sk_unprotect:
|
||||
* @sk: the secret key
|
||||
* @pw: the passphrase
|
||||
*
|
||||
* Unprotect the given secret key with the passphrase.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_sk_unprotect (cdk_pkt_seckey_t sk, const char *pw)
|
||||
{
|
||||
gcry_cipher_hd_t hd;
|
||||
cdk_dek_t dek = NULL;
|
||||
byte *data = NULL;
|
||||
u16 chksum = 0;
|
||||
size_t ndata, nbits, nbytes;
|
||||
int i, dlen, pos = 0, nskey;
|
||||
cdk_error_t rc;
|
||||
gcry_error_t err;
|
||||
|
||||
if (!sk)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
nskey = cdk_pk_get_nskey (sk->pubkey_algo);
|
||||
if (!sk->is_protected)
|
||||
{
|
||||
chksum = 0;
|
||||
for (i = 0; i < nskey; i++)
|
||||
chksum += checksum_mpi (sk->mpi[i]);
|
||||
if (chksum != sk->csum)
|
||||
return CDK_Chksum_Error;
|
||||
}
|
||||
|
||||
rc = cdk_dek_from_passphrase (&dek, sk->protect.algo,
|
||||
sk->protect.s2k, 0, pw);
|
||||
if (rc)
|
||||
return rc;
|
||||
err = gcry_cipher_open (&hd, sk->protect.algo, GCRY_CIPHER_MODE_CFB,
|
||||
GCRY_CIPHER_ENABLE_SYNC);
|
||||
if (!err)
|
||||
err = gcry_cipher_setiv (hd, sk->protect.iv, sk->protect.ivlen);
|
||||
if (!err)
|
||||
err = gcry_cipher_setkey (hd, dek->key, dek->keylen);
|
||||
if (err)
|
||||
{
|
||||
cdk_free (dek);
|
||||
return map_gcry_error (err);
|
||||
}
|
||||
cdk_dek_free (dek);
|
||||
chksum = 0;
|
||||
if (sk->version == 4)
|
||||
{
|
||||
ndata = sk->enclen;
|
||||
data = cdk_salloc (ndata, 1);
|
||||
if (!data)
|
||||
return CDK_Out_Of_Core;
|
||||
gcry_cipher_decrypt (hd, data, ndata, sk->encdata, ndata);
|
||||
if (sk->protect.sha1chk)
|
||||
{
|
||||
/* This is the new SHA1 checksum method to detect tampering
|
||||
with the key as used by the Klima/Rosa attack */
|
||||
sk->csum = 0;
|
||||
chksum = 1;
|
||||
dlen = gcry_md_get_algo_dlen (GCRY_MD_SHA1);
|
||||
if (ndata < dlen)
|
||||
{
|
||||
cdk_free (data);
|
||||
return CDK_Inv_Packet;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte mdcheck[20];
|
||||
|
||||
gcry_md_hash_buffer (GCRY_MD_SHA1, mdcheck, data, ndata - dlen);
|
||||
if (!memcmp (mdcheck, data + ndata - dlen, dlen))
|
||||
chksum = 0; /* Digest does match */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < ndata - 2; i++)
|
||||
chksum += data[i];
|
||||
sk->csum = data[ndata - 2] << 8 | data[ndata - 1];
|
||||
}
|
||||
if (sk->csum == chksum)
|
||||
{
|
||||
for (i = 0; i < nskey; i++)
|
||||
{
|
||||
nbits = data[pos] << 8 | data[pos + 1];
|
||||
|
||||
if (gcry_mpi_scan (&sk->mpi[i], GCRYMPI_FMT_PGP, data,
|
||||
(nbits + 7) / 8 + 2, &nbytes))
|
||||
{
|
||||
wipemem (data, sk->enclen);
|
||||
cdk_free (data);
|
||||
return CDK_Wrong_Format;
|
||||
}
|
||||
gcry_mpi_set_flag (sk->mpi[i], GCRYMPI_FLAG_SECURE);
|
||||
pos += (nbits + 7) / 8 + 2;
|
||||
}
|
||||
}
|
||||
wipemem (data, sk->enclen);
|
||||
cdk_free (data);
|
||||
}
|
||||
else
|
||||
{
|
||||
byte buf[MAX_MPI_BYTES + 2];
|
||||
|
||||
chksum = 0;
|
||||
for (i = 0; i < nskey; i++)
|
||||
{
|
||||
gcry_cipher_sync (hd);
|
||||
gcry_mpi_print (GCRYMPI_FMT_PGP, buf, DIM (buf),
|
||||
&nbytes, sk->mpi[i]);
|
||||
gcry_cipher_decrypt (hd, buf + 2, nbytes - 2, NULL, 0);
|
||||
gcry_mpi_release (sk->mpi[i]);
|
||||
if (gcry_mpi_scan (&sk->mpi[i], GCRYMPI_FMT_PGP,
|
||||
buf, nbytes, &nbytes))
|
||||
return CDK_Wrong_Format;
|
||||
chksum += checksum_mpi (sk->mpi[i]);
|
||||
}
|
||||
}
|
||||
gcry_cipher_close (hd);
|
||||
if (chksum != sk->csum)
|
||||
return CDK_Chksum_Error;
|
||||
sk->is_protected = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_sk_protect:
|
||||
* @sk: the secret key
|
||||
* @pw: the passphrase to use
|
||||
*
|
||||
* Protect the given secret key with a passphrase.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_sk_protect (cdk_pkt_seckey_t sk, const char *pw)
|
||||
{
|
||||
gcry_cipher_hd_t hd = NULL;
|
||||
cdk_dek_t dek = NULL;
|
||||
cdk_s2k_t s2k;
|
||||
byte *p = NULL, buf[MAX_MPI_BYTES + 2];
|
||||
size_t enclen = 0, nskey, i, nbytes;
|
||||
size_t dlen = gcry_md_get_algo_dlen (GCRY_MD_SHA1);
|
||||
gcry_error_t err;
|
||||
cdk_error_t rc;
|
||||
|
||||
nskey = cdk_pk_get_nskey (sk->pubkey_algo);
|
||||
if (!nskey)
|
||||
return CDK_Inv_Algo;
|
||||
|
||||
rc = cdk_s2k_new (&s2k, CDK_S2K_ITERSALTED, GCRY_MD_SHA256, NULL);
|
||||
if (!rc)
|
||||
rc = cdk_dek_from_passphrase (&dek, GCRY_CIPHER_AES, s2k, 1, pw);
|
||||
if (rc)
|
||||
{
|
||||
cdk_s2k_free (s2k);
|
||||
return rc;
|
||||
}
|
||||
|
||||
for (i = 0; i < nskey; i++)
|
||||
{
|
||||
enclen += 2;
|
||||
enclen += (gcry_mpi_get_nbits (sk->mpi[i]) + 7) / 8;
|
||||
}
|
||||
p = sk->encdata = cdk_calloc (1, enclen + dlen + 1);
|
||||
if (!p)
|
||||
{
|
||||
cdk_s2k_free (s2k);
|
||||
return CDK_Out_Of_Core;
|
||||
}
|
||||
|
||||
enclen = 0;
|
||||
for (i = 0; i < nskey; i++)
|
||||
{
|
||||
if (gcry_mpi_print (GCRYMPI_FMT_PGP, buf,
|
||||
DIM (buf), &nbytes, sk->mpi[i]))
|
||||
{
|
||||
cdk_free (p);
|
||||
cdk_s2k_free (s2k);
|
||||
return CDK_Wrong_Format;
|
||||
}
|
||||
memcpy (p + enclen, buf, nbytes);
|
||||
enclen += nbytes;
|
||||
}
|
||||
|
||||
enclen += dlen;
|
||||
sk->enclen = enclen;
|
||||
sk->protect.s2k = s2k;
|
||||
sk->protect.algo = GCRY_CIPHER_AES;
|
||||
sk->protect.ivlen = gcry_cipher_get_algo_blklen (sk->protect.algo);
|
||||
gcry_randomize (sk->protect.iv, sk->protect.ivlen, GCRY_STRONG_RANDOM);
|
||||
err = gcry_cipher_open (&hd, sk->protect.algo, GCRY_CIPHER_MODE_CFB,
|
||||
GCRY_CIPHER_ENABLE_SYNC);
|
||||
if (err)
|
||||
{
|
||||
cdk_dek_free (dek);
|
||||
rc = map_gcry_error (err);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
err = gcry_cipher_setkey (hd, dek->key, dek->keylen);
|
||||
if (!err)
|
||||
err = gcry_cipher_setiv (hd, sk->protect.iv, sk->protect.ivlen);
|
||||
cdk_dek_free (dek);
|
||||
if (err)
|
||||
{
|
||||
rc = map_gcry_error (err);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
sk->protect.sha1chk = 1;
|
||||
sk->is_protected = 1;
|
||||
sk->csum = 0;
|
||||
|
||||
gcry_md_hash_buffer (GCRY_MD_SHA1, buf, p, enclen - dlen);
|
||||
memcpy (p + enclen - dlen, buf, dlen);
|
||||
gcry_cipher_encrypt (hd, p, enclen, NULL, 0);
|
||||
|
||||
/* FIXME: We should release all MPI's and set the elements to NULL. */
|
||||
|
||||
leave:
|
||||
gcry_cipher_close (hd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pk_from_secret_key:
|
||||
* @sk: the secret key
|
||||
* @ret_pk: the new public key
|
||||
*
|
||||
* Create a new public key from a secret key.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_pk_from_secret_key (cdk_pkt_seckey_t sk, cdk_pubkey_t * ret_pk)
|
||||
{
|
||||
if (!sk)
|
||||
return CDK_Inv_Value;
|
||||
return _cdk_copy_pubkey (ret_pk, sk->pk);
|
||||
}
|
||||
|
||||
|
||||
#if 0 /* FIXME: Code is not finished yet. */
|
||||
cdk_error_t
|
||||
cdk_pk_revoke_cert_create (cdk_pkt_seckey_t sk, int code, const char *inf,
|
||||
char **ret_revcert)
|
||||
{
|
||||
gcry_md_hd_t md;
|
||||
cdk_subpkt_t node;
|
||||
cdk_pkt_signature_t sig;
|
||||
char *p = NULL, *dat;
|
||||
gcry_error_t err;
|
||||
cdk_error_t rc = 0;
|
||||
size_t n;
|
||||
|
||||
if (!sk || !ret_revcert)
|
||||
return CDK_Inv_Value;
|
||||
if (code < 0 || code > 3)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
sig = cdk_calloc (1, sizeof *sig);
|
||||
if (!sig)
|
||||
return CDK_Out_Of_Core;
|
||||
_cdk_sig_create (sk->pk, sig);
|
||||
n = 1;
|
||||
if (inf)
|
||||
{
|
||||
n += strlen (p);
|
||||
p = cdk_utf8_encode (inf);
|
||||
}
|
||||
dat = cdk_calloc (1, n + 1);
|
||||
if (!dat)
|
||||
{
|
||||
_cdk_free_signature (sig);
|
||||
return CDK_Out_Of_Core;
|
||||
}
|
||||
dat[0] = code;
|
||||
if (inf)
|
||||
memcpy (dat + 1, p, strlen (p));
|
||||
cdk_free (p);
|
||||
|
||||
node = cdk_subpkt_new (n);
|
||||
if (node)
|
||||
{
|
||||
cdk_subpkt_init (node, CDK_SIGSUBPKT_REVOC_REASON, dat, n);
|
||||
cdk_subpkt_add (sig->hashed, node);
|
||||
}
|
||||
cdk_free (dat);
|
||||
|
||||
err = gcry_md_open (&md, GCRY_MD_SHA1, 0);
|
||||
if (err)
|
||||
rc = map_gcry_error (err);
|
||||
else
|
||||
_cdk_hash_pubkey (sk->pk, md, 0);
|
||||
_cdk_free_signature (sig);
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
_cdk_sk_get_csum (cdk_pkt_seckey_t sk)
|
||||
{
|
||||
u16 csum = 0, i;
|
||||
|
||||
if (!sk)
|
||||
return 0;
|
||||
for (i = 0; i < cdk_pk_get_nskey (sk->pubkey_algo); i++)
|
||||
csum += checksum_mpi (sk->mpi[i]);
|
||||
return csum;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pk_get_fingerprint:
|
||||
* @pk: the public key
|
||||
* @fpr: the buffer to hold the fingerprint
|
||||
*
|
||||
* Return the fingerprint of the given public key.
|
||||
* The buffer must be at least 20 octets.
|
||||
* This function should be considered deprecated and
|
||||
* the new cdk_pk_to_fingerprint() should be used whenever
|
||||
* possible to avoid overflows.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_pk_get_fingerprint (cdk_pubkey_t pk, byte * fpr)
|
||||
{
|
||||
gcry_md_hd_t hd;
|
||||
int md_algo;
|
||||
int dlen = 0;
|
||||
gcry_error_t err;
|
||||
|
||||
if (!pk || !fpr)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (pk->version < 4 && is_RSA (pk->pubkey_algo))
|
||||
md_algo = GCRY_MD_MD5; /* special */
|
||||
else
|
||||
md_algo = GCRY_MD_SHA1;
|
||||
dlen = gcry_md_get_algo_dlen (md_algo);
|
||||
err = gcry_md_open (&hd, md_algo, 0);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
_cdk_hash_pubkey (pk, hd, 1);
|
||||
gcry_md_final (hd);
|
||||
memcpy (fpr, gcry_md_read (hd, md_algo), dlen);
|
||||
gcry_md_close (hd);
|
||||
if (dlen == 16)
|
||||
memset (fpr + 16, 0, 4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pk_to_fingerprint:
|
||||
* @pk: the public key
|
||||
* @fprbuf: buffer to save the fingerprint
|
||||
* @fprbuflen: buffer size
|
||||
* @r_nout: actual length of the fingerprint.
|
||||
*
|
||||
* Calculate a fingerprint of the given key and
|
||||
* return it in the given byte array.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_pk_to_fingerprint (cdk_pubkey_t pk,
|
||||
byte * fprbuf, size_t fprbuflen, size_t * r_nout)
|
||||
{
|
||||
size_t key_fprlen;
|
||||
cdk_error_t err;
|
||||
|
||||
if (!pk)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (pk->version < 4)
|
||||
key_fprlen = 16;
|
||||
else
|
||||
key_fprlen = 20;
|
||||
|
||||
/* Only return the required buffer size for the fingerprint. */
|
||||
if (!fprbuf && !fprbuflen && r_nout)
|
||||
{
|
||||
*r_nout = key_fprlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!fprbuf || key_fprlen > fprbuflen)
|
||||
return CDK_Too_Short;
|
||||
|
||||
err = cdk_pk_get_fingerprint (pk, fprbuf);
|
||||
if (r_nout)
|
||||
*r_nout = key_fprlen;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pk_fingerprint_get_keyid:
|
||||
* @fpr: the key fingerprint
|
||||
* @fprlen: the length of the fingerprint
|
||||
*
|
||||
* Derive the key ID from the key fingerprint.
|
||||
* For version 3 keys, this is not working.
|
||||
**/
|
||||
u32
|
||||
cdk_pk_fingerprint_get_keyid (const byte * fpr, size_t fprlen, u32 * keyid)
|
||||
{
|
||||
u32 lowbits = 0;
|
||||
|
||||
/* In this case we say the key is a V3 RSA key and we can't
|
||||
use the fingerprint to get the keyid. */
|
||||
if (fpr && fprlen == 16)
|
||||
{
|
||||
keyid[0] = 0;
|
||||
keyid[1] = 0;
|
||||
return 0;
|
||||
}
|
||||
else if (keyid && fpr)
|
||||
{
|
||||
keyid[0] = _cdk_buftou32 (fpr + 12);
|
||||
keyid[1] = _cdk_buftou32 (fpr + 16);
|
||||
lowbits = keyid[1];
|
||||
}
|
||||
else if (fpr)
|
||||
lowbits = _cdk_buftou32 (fpr + 16);
|
||||
return lowbits;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pk_get_keyid:
|
||||
* @pk: the public key
|
||||
* @keyid: buffer to store the key ID
|
||||
*
|
||||
* Calculate the key ID of the given public key.
|
||||
**/
|
||||
u32
|
||||
cdk_pk_get_keyid (cdk_pubkey_t pk, u32 * keyid)
|
||||
{
|
||||
u32 lowbits = 0;
|
||||
byte buf[24];
|
||||
|
||||
if (pk && (!pk->keyid[0] || !pk->keyid[1]))
|
||||
{
|
||||
if (pk->version < 4 && is_RSA (pk->pubkey_algo))
|
||||
{
|
||||
byte p[MAX_MPI_BYTES];
|
||||
size_t n;
|
||||
|
||||
gcry_mpi_print (GCRYMPI_FMT_USG, p, MAX_MPI_BYTES, &n, pk->mpi[0]);
|
||||
pk->keyid[0] =
|
||||
p[n - 8] << 24 | p[n - 7] << 16 | p[n - 6] << 8 | p[n - 5];
|
||||
pk->keyid[1] =
|
||||
p[n - 4] << 24 | p[n - 3] << 16 | p[n - 2] << 8 | p[n - 1];
|
||||
}
|
||||
else if (pk->version == 4)
|
||||
{
|
||||
cdk_pk_get_fingerprint (pk, buf);
|
||||
pk->keyid[0] = _cdk_buftou32 (buf + 12);
|
||||
pk->keyid[1] = _cdk_buftou32 (buf + 16);
|
||||
}
|
||||
}
|
||||
lowbits = pk ? pk->keyid[1] : 0;
|
||||
if (keyid && pk)
|
||||
{
|
||||
keyid[0] = pk->keyid[0];
|
||||
keyid[1] = pk->keyid[1];
|
||||
}
|
||||
|
||||
return lowbits;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_sk_get_keyid:
|
||||
* @sk: the secret key
|
||||
* @keyid: buffer to hold the key ID
|
||||
*
|
||||
* Calculate the key ID of the secret key, actually the public key.
|
||||
**/
|
||||
u32
|
||||
cdk_sk_get_keyid (cdk_pkt_seckey_t sk, u32 * keyid)
|
||||
{
|
||||
u32 lowbits = 0;
|
||||
|
||||
if (sk && sk->pk)
|
||||
{
|
||||
lowbits = cdk_pk_get_keyid (sk->pk, keyid);
|
||||
sk->keyid[0] = sk->pk->keyid[0];
|
||||
sk->keyid[1] = sk->pk->keyid[1];
|
||||
}
|
||||
|
||||
return lowbits;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_sig_get_keyid:
|
||||
* @sig: the signature
|
||||
* @keyid: buffer to hold the key ID
|
||||
*
|
||||
* Retrieve the key ID from the given signature.
|
||||
**/
|
||||
u32
|
||||
cdk_sig_get_keyid (cdk_pkt_signature_t sig, u32 * keyid)
|
||||
{
|
||||
u32 lowbits = sig ? sig->keyid[1] : 0;
|
||||
|
||||
if (keyid && sig)
|
||||
{
|
||||
keyid[0] = sig->keyid[0];
|
||||
keyid[1] = sig->keyid[1];
|
||||
}
|
||||
return lowbits;
|
||||
}
|
||||
|
||||
|
||||
/* Return the key ID from the given packet.
|
||||
If this is not possible, 0 is returned */
|
||||
u32
|
||||
_cdk_pkt_get_keyid (cdk_packet_t pkt, u32 * keyid)
|
||||
{
|
||||
u32 lowbits;
|
||||
|
||||
if (!pkt)
|
||||
return 0;
|
||||
|
||||
switch (pkt->pkttype)
|
||||
{
|
||||
case CDK_PKT_PUBLIC_KEY:
|
||||
case CDK_PKT_PUBLIC_SUBKEY:
|
||||
lowbits = cdk_pk_get_keyid (pkt->pkt.public_key, keyid);
|
||||
break;
|
||||
|
||||
case CDK_PKT_SECRET_KEY:
|
||||
case CDK_PKT_SECRET_SUBKEY:
|
||||
lowbits = cdk_sk_get_keyid (pkt->pkt.secret_key, keyid);
|
||||
break;
|
||||
|
||||
case CDK_PKT_SIGNATURE:
|
||||
lowbits = cdk_sig_get_keyid (pkt->pkt.signature, keyid);
|
||||
break;
|
||||
|
||||
default:
|
||||
lowbits = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return lowbits;
|
||||
}
|
||||
|
||||
|
||||
/* Get the fingerprint of the packet if possible. */
|
||||
int
|
||||
_cdk_pkt_get_fingerprint (cdk_packet_t pkt, byte * fpr)
|
||||
{
|
||||
if (!pkt || !fpr)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
switch (pkt->pkttype)
|
||||
{
|
||||
case CDK_PKT_PUBLIC_KEY:
|
||||
case CDK_PKT_PUBLIC_SUBKEY:
|
||||
return cdk_pk_get_fingerprint (pkt->pkt.public_key, fpr);
|
||||
|
||||
case CDK_PKT_SECRET_KEY:
|
||||
case CDK_PKT_SECRET_SUBKEY:
|
||||
return cdk_pk_get_fingerprint (pkt->pkt.secret_key->pk, fpr);
|
||||
|
||||
default:
|
||||
return CDK_Inv_Mode;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pubkey_to_sexp:
|
||||
* @pk: the public key
|
||||
* @sexp: where to store the S-expression
|
||||
* @len: the length of sexp
|
||||
*
|
||||
* Convert a public key to an S-expression. sexp is allocated by this
|
||||
* function, but you have to cdk_free() it yourself. The S-expression
|
||||
* is stored in canonical format as used by libgcrypt
|
||||
* (GCRYSEXP_FMT_CANON).
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_pubkey_to_sexp (cdk_pubkey_t pk, char **sexp, size_t * len)
|
||||
{
|
||||
char *buf;
|
||||
size_t sexp_len;
|
||||
gcry_sexp_t pk_sexp;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!pk || !sexp)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
rc = pubkey_to_sexp (&pk_sexp, pk);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
sexp_len = gcry_sexp_sprint (pk_sexp, GCRYSEXP_FMT_CANON, NULL, 0);
|
||||
if (!sexp_len)
|
||||
return CDK_Wrong_Format;
|
||||
|
||||
buf = (char *) cdk_malloc (sexp_len);
|
||||
if (!buf)
|
||||
{
|
||||
gcry_sexp_release (pk_sexp);
|
||||
return CDK_Out_Of_Core;
|
||||
}
|
||||
|
||||
sexp_len = gcry_sexp_sprint (pk_sexp, GCRYSEXP_FMT_CANON, buf, sexp_len);
|
||||
gcry_sexp_release (pk_sexp);
|
||||
if (!sexp_len)
|
||||
{
|
||||
cdk_free (buf);
|
||||
return CDK_Wrong_Format;
|
||||
}
|
||||
|
||||
if (len)
|
||||
*len = sexp_len;
|
||||
*sexp = buf;
|
||||
return CDK_Success;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_seckey_to_sexp:
|
||||
* @sk: the secret key
|
||||
* @sexp: where to store the S-expression
|
||||
* @len: the length of sexp
|
||||
*
|
||||
* Convert a public key to an S-expression. sexp is allocated by this
|
||||
* function, but you have to cdk_free() it yourself. The S-expression
|
||||
* is stored in canonical format as used by libgcrypt
|
||||
* (GCRYSEXP_FMT_CANON).
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_seckey_to_sexp (cdk_pkt_seckey_t sk, char **sexp, size_t * len)
|
||||
{
|
||||
char *buf;
|
||||
size_t sexp_len;
|
||||
gcry_sexp_t sk_sexp;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!sk || !sexp)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
rc = seckey_to_sexp (&sk_sexp, sk);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
sexp_len = gcry_sexp_sprint (sk_sexp, GCRYSEXP_FMT_CANON, NULL, 0);
|
||||
if (!sexp_len)
|
||||
return CDK_Wrong_Format;
|
||||
|
||||
buf = (char *) cdk_malloc (sexp_len);
|
||||
if (!buf)
|
||||
{
|
||||
gcry_sexp_release (sk_sexp);
|
||||
return CDK_Out_Of_Core;
|
||||
}
|
||||
|
||||
sexp_len = gcry_sexp_sprint (sk_sexp, GCRYSEXP_FMT_CANON, buf, sexp_len);
|
||||
gcry_sexp_release (sk_sexp);
|
||||
if (!sexp_len)
|
||||
{
|
||||
cdk_free (buf);
|
||||
return CDK_Wrong_Format;
|
||||
}
|
||||
|
||||
if (len)
|
||||
*len = sexp_len;
|
||||
*sexp = buf;
|
||||
|
||||
return CDK_Success;
|
||||
}
|
||||
@@ -1,1179 +0,0 @@
|
||||
/* read-packet.c - Read OpenPGP packets
|
||||
* Copyright (C) 2001, 2002, 2003, 2007 Timo Schulz
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "opencdk.h"
|
||||
#include "main.h"
|
||||
#include "packet.h"
|
||||
#include "types.h"
|
||||
|
||||
|
||||
/* The version of the MDC packet considering the lastest OpenPGP draft. */
|
||||
#define MDC_PKT_VER 1
|
||||
|
||||
static int
|
||||
stream_read (cdk_stream_t s, void *buf, size_t buflen, size_t * r_nread)
|
||||
{
|
||||
*r_nread = cdk_stream_read (s, buf, buflen);
|
||||
return *r_nread > 0 ? 0 : _cdk_stream_get_errno (s);
|
||||
}
|
||||
|
||||
|
||||
/* Try to read 4 octets from the stream. */
|
||||
static u32
|
||||
read_32 (cdk_stream_t s)
|
||||
{
|
||||
byte buf[4];
|
||||
size_t nread;
|
||||
|
||||
assert (s != NULL);
|
||||
|
||||
stream_read (s, buf, 4, &nread);
|
||||
if (nread != 4)
|
||||
return (u32) - 1;
|
||||
return buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
|
||||
}
|
||||
|
||||
|
||||
/* Try to read 2 octets from a stream. */
|
||||
static u16
|
||||
read_16 (cdk_stream_t s)
|
||||
{
|
||||
byte buf[2];
|
||||
size_t nread;
|
||||
|
||||
assert (s != NULL);
|
||||
|
||||
stream_read (s, buf, 2, &nread);
|
||||
if (nread != 2)
|
||||
return (u16) - 1;
|
||||
return buf[0] << 8 | buf[1];
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
read_s2k (cdk_stream_t inp, cdk_s2k_t s2k)
|
||||
{
|
||||
size_t nread;
|
||||
|
||||
if (!inp || !s2k)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("read_s2k:\n");
|
||||
|
||||
s2k->mode = cdk_stream_getc (inp);
|
||||
if (cdk_stream_eof (inp))
|
||||
return CDK_Inv_Packet;
|
||||
if (s2k->mode != CDK_S2K_SIMPLE && s2k->mode != CDK_S2K_SALTED &&
|
||||
s2k->mode != CDK_S2K_ITERSALTED)
|
||||
return CDK_Inv_Packet;
|
||||
s2k->hash_algo = cdk_stream_getc (inp);
|
||||
if (s2k->mode == CDK_S2K_SIMPLE) /* No additional elements. */
|
||||
memset (s2k->salt, 0, sizeof (s2k->salt));
|
||||
else if (s2k->mode == CDK_S2K_SALTED || s2k->mode == CDK_S2K_ITERSALTED)
|
||||
{
|
||||
if (stream_read (inp, s2k->salt, DIM (s2k->salt), &nread))
|
||||
return CDK_Inv_Packet;
|
||||
if (nread != DIM (s2k->salt))
|
||||
return CDK_Inv_Packet;
|
||||
if (s2k->mode == CDK_S2K_ITERSALTED)
|
||||
{
|
||||
s2k->count = cdk_stream_getc (inp);
|
||||
if (cdk_stream_eof (inp))
|
||||
return CDK_Inv_Packet;
|
||||
}
|
||||
}
|
||||
else
|
||||
return CDK_Inv_Mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
read_mpi (cdk_stream_t inp, gcry_mpi_t * ret_m, int secure)
|
||||
{
|
||||
gcry_mpi_t m;
|
||||
gcry_error_t err;
|
||||
byte buf[MAX_MPI_BYTES + 2];
|
||||
size_t nread, nbits;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!inp || !ret_m)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
*ret_m = NULL;
|
||||
nbits = read_16 (inp);
|
||||
nread = (nbits + 7) / 8;
|
||||
|
||||
if (nbits > MAX_MPI_BITS || nbits == 0)
|
||||
{
|
||||
_cdk_log_debug ("read_mpi: too large %d bits\n", nbits);
|
||||
return CDK_MPI_Error; /* Sanity check */
|
||||
}
|
||||
|
||||
rc = stream_read (inp, buf + 2, nread, &nread);
|
||||
if (!rc && nread != ((nbits + 7) / 8))
|
||||
{
|
||||
_cdk_log_debug ("read_mpi: too short %d < %d\n", nread,
|
||||
(nbits + 7) / 8);
|
||||
return CDK_MPI_Error;
|
||||
}
|
||||
|
||||
buf[0] = nbits >> 8;
|
||||
buf[1] = nbits >> 0;
|
||||
err = gcry_mpi_scan (&m, GCRYMPI_FMT_PGP, buf, nread + 2, &nread);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
if (secure)
|
||||
gcry_mpi_set_flag (m, GCRYMPI_FLAG_SECURE);
|
||||
*ret_m = m;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Read the encoded packet length directly from the file
|
||||
object INP and return it. Reset RET_PARTIAL if this is
|
||||
the last packet in block mode. */
|
||||
size_t
|
||||
_cdk_pkt_read_len (FILE * inp, size_t * ret_partial)
|
||||
{
|
||||
int c1, c2;
|
||||
size_t pktlen;
|
||||
|
||||
c1 = fgetc (inp);
|
||||
if (c1 == EOF)
|
||||
return (size_t) EOF;
|
||||
if (c1 < 224 || c1 == 255)
|
||||
*ret_partial = 0; /* End of partial data */
|
||||
if (c1 < 192)
|
||||
pktlen = c1;
|
||||
else if (c1 >= 192 && c1 <= 223)
|
||||
{
|
||||
c2 = fgetc (inp);
|
||||
if (c2 == EOF)
|
||||
return (size_t) EOF;
|
||||
pktlen = ((c1 - 192) << 8) + c2 + 192;
|
||||
}
|
||||
else if (c1 == 255)
|
||||
{
|
||||
pktlen = fgetc (inp) << 24;
|
||||
pktlen |= fgetc (inp) << 16;
|
||||
pktlen |= fgetc (inp) << 8;
|
||||
pktlen |= fgetc (inp) << 0;
|
||||
}
|
||||
else
|
||||
pktlen = 1 << (c1 & 0x1f);
|
||||
return pktlen;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
read_encrypted (cdk_stream_t inp, size_t pktlen, cdk_pkt_encrypted_t enc,
|
||||
int is_partial, int is_mdc)
|
||||
{
|
||||
if (!inp || !enc)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("read_encrypted: %d octets\n", pktlen);
|
||||
|
||||
if (is_mdc)
|
||||
{
|
||||
int version = cdk_stream_getc (inp);
|
||||
if (version != MDC_PKT_VER)
|
||||
return CDK_Inv_Packet;
|
||||
enc->mdc_method = CDK_MD_SHA1;
|
||||
pktlen--;
|
||||
}
|
||||
/* The packet must at least contain blocksize + 2 octets. */
|
||||
if (pktlen < 10)
|
||||
return CDK_Inv_Packet;
|
||||
if (is_partial)
|
||||
_cdk_stream_set_blockmode (inp, pktlen);
|
||||
enc->len = pktlen;
|
||||
enc->buf = inp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
read_symkey_enc (cdk_stream_t inp, size_t pktlen, cdk_pkt_symkey_enc_t ske)
|
||||
{
|
||||
cdk_s2k_t s2k;
|
||||
size_t minlen;
|
||||
size_t nread, nleft;
|
||||
|
||||
if (!inp || !ske)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("read_symkey_enc: %d octets\n", pktlen);
|
||||
|
||||
ske->version = cdk_stream_getc (inp);
|
||||
if (ske->version != 4 || cdk_stream_eof (inp))
|
||||
return CDK_Inv_Packet;
|
||||
|
||||
s2k = ske->s2k = cdk_calloc (1, sizeof *ske->s2k);
|
||||
if (!ske->s2k)
|
||||
return CDK_Out_Of_Core;
|
||||
|
||||
ske->cipher_algo = cdk_stream_getc (inp);
|
||||
s2k->mode = cdk_stream_getc (inp);
|
||||
switch (s2k->mode)
|
||||
{
|
||||
case CDK_S2K_SIMPLE:
|
||||
minlen = 0;
|
||||
break;
|
||||
case CDK_S2K_SALTED:
|
||||
minlen = 8;
|
||||
break;
|
||||
case CDK_S2K_ITERSALTED:
|
||||
minlen = 9;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Invalid S2K mode. */
|
||||
return CDK_Inv_Packet;
|
||||
}
|
||||
|
||||
s2k->hash_algo = cdk_stream_getc (inp);
|
||||
if (s2k->mode == CDK_S2K_SALTED || s2k->mode == CDK_S2K_ITERSALTED)
|
||||
{
|
||||
if (stream_read (inp, s2k->salt, DIM (s2k->salt), &nread))
|
||||
return CDK_Inv_Packet;
|
||||
if (nread != DIM (s2k->salt))
|
||||
return CDK_Inv_Packet;
|
||||
|
||||
if (s2k->mode == CDK_S2K_ITERSALTED)
|
||||
s2k->count = cdk_stream_getc (inp);
|
||||
}
|
||||
|
||||
ske->seskeylen = pktlen - 4 - minlen;
|
||||
/* We check if there is an encrypted session key and if it fits into
|
||||
the buffer. The maximal key length is 256-bit. */
|
||||
if (ske->seskeylen > DIM (ske->seskey))
|
||||
return CDK_Inv_Packet;
|
||||
nleft = ske->seskeylen;
|
||||
for (nread = 0; nread < ske->seskeylen; nread++)
|
||||
{
|
||||
ske->seskey[nread] = cdk_stream_getc (inp);
|
||||
if (cdk_stream_eof (inp) && --nleft > 0)
|
||||
return CDK_Inv_Packet;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
read_pubkey_enc (cdk_stream_t inp, size_t pktlen, cdk_pkt_pubkey_enc_t pke)
|
||||
{
|
||||
size_t i, nenc;
|
||||
|
||||
if (!inp || !pke)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("read_pubkey_enc: %d octets\n", pktlen);
|
||||
|
||||
if (pktlen < 12)
|
||||
return CDK_Inv_Packet;
|
||||
pke->version = cdk_stream_getc (inp);
|
||||
if (pke->version < 2 || pke->version > 3)
|
||||
return CDK_Inv_Packet;
|
||||
pke->keyid[0] = read_32 (inp);
|
||||
pke->keyid[1] = read_32 (inp);
|
||||
if (!pke->keyid[0] && !pke->keyid[1])
|
||||
pke->throw_keyid = 1; /* RFC2440 "speculative" keyID */
|
||||
pke->pubkey_algo = cdk_stream_getc (inp);
|
||||
nenc = cdk_pk_get_nenc (pke->pubkey_algo);
|
||||
if (!nenc)
|
||||
return CDK_Inv_Algo;
|
||||
for (i = 0; i < nenc; i++)
|
||||
{
|
||||
cdk_error_t rc = read_mpi (inp, &pke->mpi[i], 0);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
read_mdc (cdk_stream_t inp, cdk_pkt_mdc_t mdc)
|
||||
{
|
||||
size_t n;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!inp || !mdc)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("read_mdc:\n");
|
||||
|
||||
rc = stream_read (inp, mdc->hash, DIM (mdc->hash), &n);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return n != DIM (mdc->hash) ? CDK_Inv_Packet : 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
read_compressed (cdk_stream_t inp, size_t pktlen, cdk_pkt_compressed_t c)
|
||||
{
|
||||
if (!inp || !c)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("read_compressed: %d octets\n", pktlen);
|
||||
|
||||
c->algorithm = cdk_stream_getc (inp);
|
||||
if (c->algorithm > 3)
|
||||
return CDK_Inv_Packet;
|
||||
|
||||
/* don't know the size, so we read until EOF */
|
||||
if (!pktlen)
|
||||
{
|
||||
c->len = 0;
|
||||
c->buf = inp;
|
||||
}
|
||||
|
||||
/* FIXME: Support partial bodies. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
read_public_key (cdk_stream_t inp, size_t pktlen, cdk_pkt_pubkey_t pk)
|
||||
{
|
||||
size_t i, ndays, npkey;
|
||||
|
||||
if (!inp || !pk)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("read_public_key: %d octets\n", pktlen);
|
||||
|
||||
pk->is_invalid = 1; /* default to detect missing self signatures */
|
||||
pk->is_revoked = 0;
|
||||
pk->has_expired = 0;
|
||||
|
||||
pk->version = cdk_stream_getc (inp);
|
||||
if (pk->version < 2 || pk->version > 4)
|
||||
return CDK_Inv_Packet_Ver;
|
||||
pk->timestamp = read_32 (inp);
|
||||
if (pk->version < 4)
|
||||
{
|
||||
ndays = read_16 (inp);
|
||||
if (ndays)
|
||||
pk->expiredate = pk->timestamp + ndays * 86400L;
|
||||
}
|
||||
|
||||
pk->pubkey_algo = cdk_stream_getc (inp);
|
||||
npkey = cdk_pk_get_npkey (pk->pubkey_algo);
|
||||
if (!npkey)
|
||||
{
|
||||
_cdk_log_debug ("invalid public key algorithm %d\n", pk->pubkey_algo);
|
||||
return CDK_Inv_Algo;
|
||||
}
|
||||
for (i = 0; i < npkey; i++)
|
||||
{
|
||||
cdk_error_t rc = read_mpi (inp, &pk->mpi[i], 0);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* These values are just for the first run and should be
|
||||
replaced with the actual key flags from the self signature. */
|
||||
pk->pubkey_usage = _cdk_pk_algo_usage (pk->pubkey_algo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
read_public_subkey (cdk_stream_t inp, size_t pktlen, cdk_pkt_pubkey_t pk)
|
||||
{
|
||||
if (!inp || !pk)
|
||||
return CDK_Inv_Value;
|
||||
return read_public_key (inp, pktlen, pk);
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
read_secret_key (cdk_stream_t inp, size_t pktlen, cdk_pkt_seckey_t sk)
|
||||
{
|
||||
size_t p1, p2, nread;
|
||||
int i, nskey;
|
||||
int rc;
|
||||
|
||||
if (!inp || !sk || !sk->pk)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("read_secret_key: %d octets\n", pktlen);
|
||||
|
||||
p1 = cdk_stream_tell (inp);
|
||||
rc = read_public_key (inp, pktlen, sk->pk);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
sk->s2k_usage = cdk_stream_getc (inp);
|
||||
sk->protect.sha1chk = 0;
|
||||
if (sk->s2k_usage == 254 || sk->s2k_usage == 255)
|
||||
{
|
||||
sk->protect.sha1chk = (sk->s2k_usage == 254);
|
||||
sk->protect.algo = cdk_stream_getc (inp);
|
||||
sk->protect.s2k = cdk_calloc (1, sizeof *sk->protect.s2k);
|
||||
if (!sk->protect.s2k)
|
||||
return CDK_Out_Of_Core;
|
||||
rc = read_s2k (inp, sk->protect.s2k);
|
||||
if (rc)
|
||||
return rc;
|
||||
sk->protect.ivlen = gcry_cipher_get_algo_blklen (sk->protect.algo);
|
||||
if (!sk->protect.ivlen)
|
||||
return CDK_Inv_Packet;
|
||||
rc = stream_read (inp, sk->protect.iv, sk->protect.ivlen, &nread);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (nread != sk->protect.ivlen)
|
||||
return CDK_Inv_Packet;
|
||||
}
|
||||
else
|
||||
sk->protect.algo = sk->s2k_usage;
|
||||
if (sk->protect.algo == GCRY_CIPHER_NONE)
|
||||
{
|
||||
sk->csum = 0;
|
||||
nskey = cdk_pk_get_nskey (sk->pk->pubkey_algo);
|
||||
if (!nskey)
|
||||
return CDK_Inv_Algo;
|
||||
for (i = 0; i < nskey; i++)
|
||||
{
|
||||
rc = read_mpi (inp, &sk->mpi[i], 1);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
sk->csum = read_16 (inp);
|
||||
sk->is_protected = 0;
|
||||
}
|
||||
else if (sk->pk->version < 4)
|
||||
{
|
||||
/* The length of each multiprecision integer is stored in plaintext. */
|
||||
nskey = cdk_pk_get_nskey (sk->pk->pubkey_algo);
|
||||
if (!nskey)
|
||||
return CDK_Inv_Algo;
|
||||
for (i = 0; i < nskey; i++)
|
||||
{
|
||||
rc = read_mpi (inp, &sk->mpi[i], 1);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
sk->csum = read_16 (inp);
|
||||
sk->is_protected = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We need to read the rest of the packet because we do not
|
||||
have any information how long the encrypted mpi's are */
|
||||
p2 = cdk_stream_tell (inp);
|
||||
p2 -= p1;
|
||||
sk->enclen = pktlen - p2;
|
||||
if (sk->enclen < 2)
|
||||
return CDK_Inv_Packet; /* at least 16 bits for the checksum! */
|
||||
sk->encdata = cdk_calloc (1, sk->enclen + 1);
|
||||
if (!sk->encdata)
|
||||
return CDK_Out_Of_Core;
|
||||
if (stream_read (inp, sk->encdata, sk->enclen, &nread))
|
||||
return CDK_Inv_Packet;
|
||||
nskey = cdk_pk_get_nskey (sk->pk->pubkey_algo);
|
||||
if (!nskey)
|
||||
return CDK_Inv_Algo;
|
||||
/* We mark each MPI entry with NULL to indicate a protected key. */
|
||||
for (i = 0; i < nskey; i++)
|
||||
sk->mpi[i] = NULL;
|
||||
sk->is_protected = 1;
|
||||
}
|
||||
|
||||
sk->is_primary = 1;
|
||||
_cdk_copy_pk_to_sk (sk->pk, sk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
read_secret_subkey (cdk_stream_t inp, size_t pktlen, cdk_pkt_seckey_t sk)
|
||||
{
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!inp || !sk || !sk->pk)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
rc = read_secret_key (inp, pktlen, sk);
|
||||
sk->is_primary = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
read_attribute (cdk_stream_t inp, size_t pktlen, cdk_pkt_userid_t attr)
|
||||
{
|
||||
const byte *p;
|
||||
byte *buf;
|
||||
size_t len, nread;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!inp || !attr || !pktlen)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("read_attribute: %d octets\n", pktlen);
|
||||
|
||||
strcpy (attr->name, "[attribute]");
|
||||
attr->len = strlen (attr->name);
|
||||
buf = cdk_calloc (1, pktlen);
|
||||
if (!buf)
|
||||
return CDK_Out_Of_Core;
|
||||
rc = stream_read (inp, buf, pktlen, &nread);
|
||||
if (rc)
|
||||
{
|
||||
cdk_free (buf);
|
||||
return CDK_Inv_Packet;
|
||||
}
|
||||
p = buf;
|
||||
len = *p++;
|
||||
pktlen--;
|
||||
if (len == 255)
|
||||
{
|
||||
len = _cdk_buftou32 (p);
|
||||
p += 4;
|
||||
pktlen -= 4;
|
||||
}
|
||||
else if (len >= 192)
|
||||
{
|
||||
if (pktlen < 2)
|
||||
{
|
||||
cdk_free (buf);
|
||||
return CDK_Inv_Packet;
|
||||
}
|
||||
len = ((len - 192) << 8) + *p + 192;
|
||||
p++;
|
||||
pktlen--;
|
||||
}
|
||||
|
||||
if (*p != 1) /* Currently only 1, meaning an image, is defined. */
|
||||
{
|
||||
cdk_free (buf);
|
||||
return CDK_Inv_Packet;
|
||||
}
|
||||
p++;
|
||||
len--;
|
||||
|
||||
if (pktlen - (len + 1) > 0)
|
||||
return CDK_Inv_Packet;
|
||||
attr->attrib_img = cdk_calloc (1, len);
|
||||
if (!attr->attrib_img)
|
||||
{
|
||||
cdk_free (buf);
|
||||
return CDK_Out_Of_Core;
|
||||
}
|
||||
attr->attrib_len = len;
|
||||
memcpy (attr->attrib_img, p, len);
|
||||
cdk_free (buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
read_user_id (cdk_stream_t inp, size_t pktlen, cdk_pkt_userid_t user_id)
|
||||
{
|
||||
size_t nread;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!inp || !user_id)
|
||||
return CDK_Inv_Value;
|
||||
if (!pktlen)
|
||||
return CDK_Inv_Packet;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("read_user_id: %lu octets\n", pktlen);
|
||||
|
||||
user_id->len = pktlen;
|
||||
rc = stream_read (inp, user_id->name, pktlen, &nread);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (nread != pktlen)
|
||||
return CDK_Inv_Packet;
|
||||
user_id->name[nread] = '\0';
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
read_subpkt (cdk_stream_t inp, cdk_subpkt_t * r_ctx, size_t * r_nbytes)
|
||||
{
|
||||
byte c, c1;
|
||||
size_t size, nread, n;
|
||||
cdk_subpkt_t node;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!inp || !r_nbytes)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("read_subpkt:\n");
|
||||
|
||||
n = 0;
|
||||
*r_nbytes = 0;
|
||||
c = cdk_stream_getc (inp);
|
||||
n++;
|
||||
if (c == 255)
|
||||
{
|
||||
size = read_32 (inp);
|
||||
n += 4;
|
||||
}
|
||||
else if (c >= 192 && c < 255)
|
||||
{
|
||||
c1 = cdk_stream_getc (inp);
|
||||
n++;
|
||||
if (c1 == 0)
|
||||
return 0;
|
||||
size = ((c - 192) << 8) + c1 + 192;
|
||||
}
|
||||
else if (c < 192)
|
||||
size = c;
|
||||
else
|
||||
return CDK_Inv_Packet;
|
||||
|
||||
node = cdk_subpkt_new (size);
|
||||
if (!node)
|
||||
return CDK_Out_Of_Core;
|
||||
node->size = size;
|
||||
node->type = cdk_stream_getc (inp);
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug (" %d octets %d type\n", node->size, node->type);
|
||||
n++;
|
||||
node->size--;
|
||||
rc = stream_read (inp, node->d, node->size, &nread);
|
||||
n += nread;
|
||||
if (rc)
|
||||
return rc;
|
||||
*r_nbytes = n;
|
||||
if (!*r_ctx)
|
||||
*r_ctx = node;
|
||||
else
|
||||
cdk_subpkt_add (*r_ctx, node);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
read_onepass_sig (cdk_stream_t inp, size_t pktlen, cdk_pkt_onepass_sig_t sig)
|
||||
{
|
||||
if (!inp || !sig)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("read_onepass_sig: %d octets\n", pktlen);
|
||||
|
||||
if (pktlen != 13)
|
||||
return CDK_Inv_Packet;
|
||||
sig->version = cdk_stream_getc (inp);
|
||||
if (sig->version != 3)
|
||||
return CDK_Inv_Packet_Ver;
|
||||
sig->sig_class = cdk_stream_getc (inp);
|
||||
sig->digest_algo = cdk_stream_getc (inp);
|
||||
sig->pubkey_algo = cdk_stream_getc (inp);
|
||||
sig->keyid[0] = read_32 (inp);
|
||||
sig->keyid[1] = read_32 (inp);
|
||||
sig->last = cdk_stream_getc (inp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
parse_sig_subpackets (cdk_pkt_signature_t sig)
|
||||
{
|
||||
cdk_subpkt_t node;
|
||||
|
||||
/* Setup the standard packet entries, so we can use V4
|
||||
signatures similar to V3. */
|
||||
for (node = sig->unhashed; node; node = node->next)
|
||||
{
|
||||
if (node->type == CDK_SIGSUBPKT_ISSUER && node->size >= 8)
|
||||
{
|
||||
sig->keyid[0] = _cdk_buftou32 (node->d);
|
||||
sig->keyid[1] = _cdk_buftou32 (node->d + 4);
|
||||
}
|
||||
else if (node->type == CDK_SIGSUBPKT_EXPORTABLE && node->d[0] == 0)
|
||||
{
|
||||
/* Sometimes this packet might be placed in the unhashed area */
|
||||
sig->flags.exportable = 0;
|
||||
}
|
||||
}
|
||||
for (node = sig->hashed; node; node = node->next)
|
||||
{
|
||||
if (node->type == CDK_SIGSUBPKT_SIG_CREATED && node->size >= 4)
|
||||
sig->timestamp = _cdk_buftou32 (node->d);
|
||||
else if (node->type == CDK_SIGSUBPKT_SIG_EXPIRE && node->size >= 4)
|
||||
{
|
||||
sig->expiredate = _cdk_buftou32 (node->d);
|
||||
if (sig->expiredate > 0 && sig->expiredate < (u32) time (NULL))
|
||||
sig->flags.expired = 1;
|
||||
}
|
||||
else if (node->type == CDK_SIGSUBPKT_POLICY)
|
||||
sig->flags.policy_url = 1;
|
||||
else if (node->type == CDK_SIGSUBPKT_NOTATION)
|
||||
sig->flags.notation = 1;
|
||||
else if (node->type == CDK_SIGSUBPKT_REVOCABLE && node->d[0] == 0)
|
||||
sig->flags.revocable = 0;
|
||||
else if (node->type == CDK_SIGSUBPKT_EXPORTABLE && node->d[0] == 0)
|
||||
sig->flags.exportable = 0;
|
||||
}
|
||||
if (sig->sig_class == 0x1F)
|
||||
{
|
||||
cdk_desig_revoker_t r, rnode;
|
||||
|
||||
for (node = sig->hashed; node; node = node->next)
|
||||
{
|
||||
if (node->type == CDK_SIGSUBPKT_REV_KEY)
|
||||
{
|
||||
if (node->size < 22)
|
||||
continue;
|
||||
rnode = cdk_calloc (1, sizeof *rnode);
|
||||
if (!rnode)
|
||||
return CDK_Out_Of_Core;
|
||||
rnode->r_class = node->d[0];
|
||||
rnode->algid = node->d[1];
|
||||
memcpy (rnode->fpr, node->d + 2, KEY_FPR_LEN);
|
||||
if (!sig->revkeys)
|
||||
sig->revkeys = rnode;
|
||||
else
|
||||
{
|
||||
for (r = sig->revkeys; r->next; r = r->next)
|
||||
;
|
||||
r->next = rnode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
read_signature (cdk_stream_t inp, size_t pktlen, cdk_pkt_signature_t sig)
|
||||
{
|
||||
size_t nbytes;
|
||||
size_t i, size, nsig;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!inp || !sig)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("read_signature: %d octets\n", pktlen);
|
||||
|
||||
if (pktlen < 16)
|
||||
return CDK_Inv_Packet;
|
||||
sig->version = cdk_stream_getc (inp);
|
||||
if (sig->version < 2 || sig->version > 4)
|
||||
return CDK_Inv_Packet_Ver;
|
||||
|
||||
sig->flags.exportable = 1;
|
||||
sig->flags.revocable = 1;
|
||||
|
||||
if (sig->version < 4)
|
||||
{
|
||||
if (cdk_stream_getc (inp) != 5)
|
||||
return CDK_Inv_Packet;
|
||||
sig->sig_class = cdk_stream_getc (inp);
|
||||
sig->timestamp = read_32 (inp);
|
||||
sig->keyid[0] = read_32 (inp);
|
||||
sig->keyid[1] = read_32 (inp);
|
||||
sig->pubkey_algo = cdk_stream_getc (inp);
|
||||
sig->digest_algo = cdk_stream_getc (inp);
|
||||
sig->digest_start[0] = cdk_stream_getc (inp);
|
||||
sig->digest_start[1] = cdk_stream_getc (inp);
|
||||
nsig = cdk_pk_get_nsig (sig->pubkey_algo);
|
||||
if (!nsig)
|
||||
return CDK_Inv_Algo;
|
||||
for (i = 0; i < nsig; i++)
|
||||
{
|
||||
rc = read_mpi (inp, &sig->mpi[i], 0);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sig->sig_class = cdk_stream_getc (inp);
|
||||
sig->pubkey_algo = cdk_stream_getc (inp);
|
||||
sig->digest_algo = cdk_stream_getc (inp);
|
||||
sig->hashed_size = read_16 (inp);
|
||||
size = sig->hashed_size;
|
||||
sig->hashed = NULL;
|
||||
while (size > 0)
|
||||
{
|
||||
rc = read_subpkt (inp, &sig->hashed, &nbytes);
|
||||
if (rc)
|
||||
return rc;
|
||||
size -= nbytes;
|
||||
}
|
||||
sig->unhashed_size = read_16 (inp);
|
||||
size = sig->unhashed_size;
|
||||
sig->unhashed = NULL;
|
||||
while (size > 0)
|
||||
{
|
||||
rc = read_subpkt (inp, &sig->unhashed, &nbytes);
|
||||
if (rc)
|
||||
return rc;
|
||||
size -= nbytes;
|
||||
}
|
||||
|
||||
rc = parse_sig_subpackets (sig);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
sig->digest_start[0] = cdk_stream_getc (inp);
|
||||
sig->digest_start[1] = cdk_stream_getc (inp);
|
||||
nsig = cdk_pk_get_nsig (sig->pubkey_algo);
|
||||
if (!nsig)
|
||||
return CDK_Inv_Algo;
|
||||
for (i = 0; i < nsig; i++)
|
||||
{
|
||||
rc = read_mpi (inp, &sig->mpi[i], 0);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
read_literal (cdk_stream_t inp, size_t pktlen,
|
||||
cdk_pkt_literal_t * ret_pt, int is_partial)
|
||||
{
|
||||
cdk_pkt_literal_t pt = *ret_pt;
|
||||
size_t nread;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!inp || !pt)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("read_literal: %d octets\n", pktlen);
|
||||
|
||||
pt->mode = cdk_stream_getc (inp);
|
||||
if (pt->mode != 0x62 && pt->mode != 0x74 && pt->mode != 0x75)
|
||||
return CDK_Inv_Packet;
|
||||
if (cdk_stream_eof (inp))
|
||||
return CDK_Inv_Packet;
|
||||
|
||||
pt->namelen = cdk_stream_getc (inp);
|
||||
if (pt->namelen > 0)
|
||||
{
|
||||
*ret_pt = pt = cdk_realloc (pt, sizeof *pt + pt->namelen + 1);
|
||||
if (!pt)
|
||||
return CDK_Out_Of_Core;
|
||||
rc = stream_read (inp, pt->name, pt->namelen, &nread);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (nread != pt->namelen)
|
||||
return CDK_Inv_Packet;
|
||||
pt->name[pt->namelen] = '\0';
|
||||
}
|
||||
pt->timestamp = read_32 (inp);
|
||||
pktlen = pktlen - 6 - pt->namelen;
|
||||
if (is_partial)
|
||||
_cdk_stream_set_blockmode (inp, pktlen);
|
||||
pt->buf = inp;
|
||||
pt->len = pktlen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Read an old packet CTB and return the length of the body. */
|
||||
static void
|
||||
read_old_length (cdk_stream_t inp, int ctb, size_t * r_len, size_t * r_size)
|
||||
{
|
||||
int llen = ctb & 0x03;
|
||||
|
||||
if (llen == 0)
|
||||
{
|
||||
*r_len = cdk_stream_getc (inp);
|
||||
(*r_size)++;
|
||||
}
|
||||
else if (llen == 1)
|
||||
{
|
||||
*r_len = read_16 (inp);
|
||||
(*r_size) += 2;
|
||||
}
|
||||
else if (llen == 2)
|
||||
{
|
||||
*r_len = read_32 (inp);
|
||||
(*r_size) += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
*r_len = 0;
|
||||
*r_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Read a new CTB and decode the body length. */
|
||||
static void
|
||||
read_new_length (cdk_stream_t inp,
|
||||
size_t * r_len, size_t * r_size, size_t * r_partial)
|
||||
{
|
||||
int c, c1;
|
||||
|
||||
c = cdk_stream_getc (inp);
|
||||
(*r_size)++;
|
||||
if (c < 192)
|
||||
*r_len = c;
|
||||
else if (c >= 192 && c <= 223)
|
||||
{
|
||||
c1 = cdk_stream_getc (inp);
|
||||
(*r_size)++;
|
||||
*r_len = ((c - 192) << 8) + c1 + 192;
|
||||
}
|
||||
else if (c == 255)
|
||||
{
|
||||
*r_len = read_32 (inp);
|
||||
(*r_size) += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
*r_len = 1 << (c & 0x1f);
|
||||
*r_partial = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Skip the current packet body. */
|
||||
static void
|
||||
skip_packet (cdk_stream_t inp, size_t pktlen)
|
||||
{
|
||||
byte buf[BUFSIZE];
|
||||
size_t nread, buflen = DIM (buf);
|
||||
|
||||
while (pktlen > 0)
|
||||
{
|
||||
stream_read (inp, buf, pktlen > buflen ? buflen : pktlen, &nread);
|
||||
pktlen -= nread;
|
||||
}
|
||||
|
||||
assert (pktlen == 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pkt_read:
|
||||
* @inp: the input stream
|
||||
* @pkt: allocated packet handle to store the packet
|
||||
*
|
||||
* Parse the next packet on the @inp stream and return its contents in @pkt.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_pkt_read (cdk_stream_t inp, cdk_packet_t pkt)
|
||||
{
|
||||
int use_mdc = 0;
|
||||
int ctb, is_newctb;
|
||||
int pkttype;
|
||||
size_t pktlen = 0, pktsize = 0, is_partial = 0;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!inp || !pkt)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
ctb = cdk_stream_getc (inp);
|
||||
if (cdk_stream_eof (inp) || ctb == EOF)
|
||||
return CDK_EOF;
|
||||
else if (!ctb)
|
||||
return CDK_Inv_Packet;
|
||||
|
||||
pktsize++;
|
||||
if (!(ctb & 0x80))
|
||||
{
|
||||
_cdk_log_info ("cdk_pkt_read: no openpgp data found. "
|
||||
"(ctb=%02X; fpos=%02X)\n", ctb, cdk_stream_tell (inp));
|
||||
return CDK_Inv_Packet;
|
||||
}
|
||||
|
||||
if (ctb & 0x40) /* RFC2440 packet format. */
|
||||
{
|
||||
pkttype = ctb & 0x3f;
|
||||
is_newctb = 1;
|
||||
}
|
||||
else /* the old RFC1991 packet format. */
|
||||
{
|
||||
pkttype = ctb & 0x3f;
|
||||
pkttype >>= 2;
|
||||
is_newctb = 0;
|
||||
}
|
||||
|
||||
if (pkttype > 63)
|
||||
{
|
||||
_cdk_log_info ("cdk_pkt_read: unknown type %d\n", pkttype);
|
||||
return CDK_Inv_Packet;
|
||||
}
|
||||
|
||||
if (is_newctb)
|
||||
read_new_length (inp, &pktlen, &pktsize, &is_partial);
|
||||
else
|
||||
read_old_length (inp, ctb, &pktlen, &pktsize);
|
||||
|
||||
pkt->pkttype = pkttype;
|
||||
pkt->pktlen = pktlen;
|
||||
pkt->pktsize = pktsize + pktlen;
|
||||
pkt->old_ctb = is_newctb ? 0 : 1;
|
||||
|
||||
rc = 0;
|
||||
switch (pkt->pkttype)
|
||||
{
|
||||
case CDK_PKT_ATTRIBUTE:
|
||||
pkt->pkt.user_id = cdk_calloc (1, sizeof *pkt->pkt.user_id
|
||||
+ pkt->pktlen + 16 + 1);
|
||||
if (!pkt->pkt.user_id)
|
||||
return CDK_Out_Of_Core;
|
||||
rc = read_attribute (inp, pktlen, pkt->pkt.user_id);
|
||||
pkt->pkttype = CDK_PKT_ATTRIBUTE;
|
||||
break;
|
||||
|
||||
case CDK_PKT_USER_ID:
|
||||
pkt->pkt.user_id = cdk_calloc (1, sizeof *pkt->pkt.user_id
|
||||
+ pkt->pktlen);
|
||||
if (!pkt->pkt.user_id)
|
||||
return CDK_Out_Of_Core;
|
||||
rc = read_user_id (inp, pktlen, pkt->pkt.user_id);
|
||||
break;
|
||||
|
||||
case CDK_PKT_PUBLIC_KEY:
|
||||
pkt->pkt.public_key = cdk_calloc (1, sizeof *pkt->pkt.public_key);
|
||||
if (!pkt->pkt.public_key)
|
||||
return CDK_Out_Of_Core;
|
||||
rc = read_public_key (inp, pktlen, pkt->pkt.public_key);
|
||||
break;
|
||||
|
||||
case CDK_PKT_PUBLIC_SUBKEY:
|
||||
pkt->pkt.public_key = cdk_calloc (1, sizeof *pkt->pkt.public_key);
|
||||
if (!pkt->pkt.public_key)
|
||||
return CDK_Out_Of_Core;
|
||||
rc = read_public_subkey (inp, pktlen, pkt->pkt.public_key);
|
||||
break;
|
||||
|
||||
case CDK_PKT_SECRET_KEY:
|
||||
pkt->pkt.secret_key = cdk_calloc (1, sizeof *pkt->pkt.secret_key);
|
||||
if (!pkt->pkt.secret_key)
|
||||
return CDK_Out_Of_Core;
|
||||
pkt->pkt.secret_key->pk = cdk_calloc (1,
|
||||
sizeof *pkt->pkt.secret_key->pk);
|
||||
if (!pkt->pkt.secret_key->pk)
|
||||
return CDK_Out_Of_Core;
|
||||
rc = read_secret_key (inp, pktlen, pkt->pkt.secret_key);
|
||||
break;
|
||||
|
||||
case CDK_PKT_SECRET_SUBKEY:
|
||||
pkt->pkt.secret_key = cdk_calloc (1, sizeof *pkt->pkt.secret_key);
|
||||
if (!pkt->pkt.secret_key)
|
||||
return CDK_Out_Of_Core;
|
||||
pkt->pkt.secret_key->pk = cdk_calloc (1,
|
||||
sizeof *pkt->pkt.secret_key->pk);
|
||||
if (!pkt->pkt.secret_key->pk)
|
||||
return CDK_Out_Of_Core;
|
||||
rc = read_secret_subkey (inp, pktlen, pkt->pkt.secret_key);
|
||||
break;
|
||||
|
||||
case CDK_PKT_LITERAL:
|
||||
pkt->pkt.literal = cdk_calloc (1, sizeof *pkt->pkt.literal);
|
||||
if (!pkt->pkt.literal)
|
||||
return CDK_Out_Of_Core;
|
||||
rc = read_literal (inp, pktlen, &pkt->pkt.literal, is_partial);
|
||||
break;
|
||||
|
||||
case CDK_PKT_ONEPASS_SIG:
|
||||
pkt->pkt.onepass_sig = cdk_calloc (1, sizeof *pkt->pkt.onepass_sig);
|
||||
if (!pkt->pkt.onepass_sig)
|
||||
return CDK_Out_Of_Core;
|
||||
rc = read_onepass_sig (inp, pktlen, pkt->pkt.onepass_sig);
|
||||
break;
|
||||
|
||||
case CDK_PKT_SIGNATURE:
|
||||
pkt->pkt.signature = cdk_calloc (1, sizeof *pkt->pkt.signature);
|
||||
if (!pkt->pkt.signature)
|
||||
return CDK_Out_Of_Core;
|
||||
rc = read_signature (inp, pktlen, pkt->pkt.signature);
|
||||
break;
|
||||
|
||||
case CDK_PKT_ENCRYPTED_MDC:
|
||||
case CDK_PKT_ENCRYPTED:
|
||||
pkt->pkt.encrypted = cdk_calloc (1, sizeof *pkt->pkt.encrypted);
|
||||
if (!pkt->pkt.encrypted)
|
||||
return CDK_Out_Of_Core;
|
||||
use_mdc = (pkt->pkttype == CDK_PKT_ENCRYPTED_MDC) ? 1 : 0;
|
||||
rc = read_encrypted (inp, pktlen, pkt->pkt.encrypted,
|
||||
is_partial, use_mdc);
|
||||
break;
|
||||
|
||||
case CDK_PKT_SYMKEY_ENC:
|
||||
pkt->pkt.symkey_enc = cdk_calloc (1, sizeof *pkt->pkt.symkey_enc);
|
||||
if (!pkt->pkt.symkey_enc)
|
||||
return CDK_Out_Of_Core;
|
||||
rc = read_symkey_enc (inp, pktlen, pkt->pkt.symkey_enc);
|
||||
break;
|
||||
|
||||
case CDK_PKT_PUBKEY_ENC:
|
||||
pkt->pkt.pubkey_enc = cdk_calloc (1, sizeof *pkt->pkt.pubkey_enc);
|
||||
if (!pkt->pkt.pubkey_enc)
|
||||
return CDK_Out_Of_Core;
|
||||
rc = read_pubkey_enc (inp, pktlen, pkt->pkt.pubkey_enc);
|
||||
break;
|
||||
|
||||
case CDK_PKT_COMPRESSED:
|
||||
pkt->pkt.compressed = cdk_calloc (1, sizeof *pkt->pkt.compressed);
|
||||
if (!pkt->pkt.compressed)
|
||||
return CDK_Out_Of_Core;
|
||||
rc = read_compressed (inp, pktlen, pkt->pkt.compressed);
|
||||
break;
|
||||
|
||||
case CDK_PKT_MDC:
|
||||
pkt->pkt.mdc = cdk_calloc (1, sizeof *pkt->pkt.mdc);
|
||||
if (!pkt->pkt.mdc)
|
||||
return CDK_Out_Of_Core;
|
||||
rc = read_mdc (inp, pkt->pkt.mdc);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Skip all packets we don't understand */
|
||||
skip_packet (inp, pktlen);
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -1,717 +0,0 @@
|
||||
/* seskey.c - Session key routines
|
||||
* Copyright (C) 2002, 2003, 2007 Timo Schulz
|
||||
* Copyright (C) 1998-2000, 2002 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <gcrypt.h>
|
||||
|
||||
#include "opencdk.h"
|
||||
#include "main.h"
|
||||
#include "packet.h"
|
||||
|
||||
|
||||
/* We encode the MD in this way:
|
||||
*
|
||||
* 0 1 PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes)
|
||||
*
|
||||
* PAD consists of FF bytes.
|
||||
*/
|
||||
static cdk_error_t
|
||||
do_encode_md (byte ** r_frame, size_t * r_flen, const byte * md, int algo,
|
||||
size_t len, unsigned nbits, const byte * asn, size_t asnlen)
|
||||
{
|
||||
byte *frame = NULL;
|
||||
size_t nframe = (nbits + 7) / 8;
|
||||
size_t i, n = 0;
|
||||
|
||||
if (!asn || !md || !r_frame || !r_flen)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (len + asnlen + 4 > nframe)
|
||||
return CDK_General_Error;
|
||||
|
||||
frame = cdk_calloc (1, nframe);
|
||||
if (!frame)
|
||||
return CDK_Out_Of_Core;
|
||||
frame[n++] = 0;
|
||||
frame[n++] = 1;
|
||||
i = nframe - len - asnlen - 3;
|
||||
if (i < 0)
|
||||
{
|
||||
cdk_free (frame);
|
||||
return CDK_Inv_Value;
|
||||
}
|
||||
memset (frame + n, 0xFF, i);
|
||||
n += i;
|
||||
frame[n++] = 0;
|
||||
memcpy (frame + n, asn, asnlen);
|
||||
n += asnlen;
|
||||
memcpy (frame + n, md, len);
|
||||
n += len;
|
||||
if (n != nframe)
|
||||
{
|
||||
cdk_free (frame);
|
||||
return CDK_Inv_Value;
|
||||
}
|
||||
*r_frame = frame;
|
||||
*r_flen = n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* RFC2437 format:
|
||||
*
|
||||
* 0 2 RND(n bytes) 0 [A DEK(k bytes) CSUM(2 bytes)]
|
||||
*
|
||||
* RND - randomized bytes for padding.
|
||||
* A - cipher algorithm.
|
||||
* DEK - random session key.
|
||||
* CKSUM - algebraic checksum of the DEK.
|
||||
*/
|
||||
|
||||
/**
|
||||
* cdk_dek_encode_pkcs1
|
||||
* @dek: DEK object
|
||||
* @nbits: size of the multi precision integer frame
|
||||
* @r_enc: output of the encoded multiprecision integer
|
||||
*
|
||||
* Encode the given random session key in the DEK object
|
||||
* into a multiprecision integer.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_dek_encode_pkcs1 (cdk_dek_t dek, size_t nbits, gcry_mpi_t * r_enc)
|
||||
{
|
||||
gcry_mpi_t a = NULL;
|
||||
gcry_error_t err;
|
||||
byte *p, *frame;
|
||||
size_t n;
|
||||
size_t nframe;
|
||||
size_t i;
|
||||
u16 chksum;
|
||||
|
||||
if (!r_enc || !dek)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
*r_enc = NULL;
|
||||
chksum = 0;
|
||||
for (i = 0; i < dek->keylen; i++)
|
||||
chksum += dek->key[i];
|
||||
nframe = (nbits + 7) / 8;
|
||||
frame = cdk_salloc (nframe + 1, 1);
|
||||
if (!frame)
|
||||
return CDK_Out_Of_Core;
|
||||
n = 0;
|
||||
frame[n++] = 0x00;
|
||||
frame[n++] = 0x02;
|
||||
i = nframe - 6 - dek->keylen;
|
||||
p = gcry_random_bytes (i, GCRY_STRONG_RANDOM);
|
||||
/* Replace zero bytes by new values */
|
||||
for (;;)
|
||||
{
|
||||
size_t j, k;
|
||||
byte *pp;
|
||||
|
||||
/* count the zero bytes */
|
||||
for (j = k = 0; j < i; j++)
|
||||
{
|
||||
if (!p[j])
|
||||
k++;
|
||||
}
|
||||
if (!k)
|
||||
break; /* No zeroes remain. */
|
||||
k += k / 128; /* better get some more */
|
||||
pp = gcry_random_bytes (k, GCRY_STRONG_RANDOM);
|
||||
for (j = 0; j < i && k; j++)
|
||||
{
|
||||
if (!p[j])
|
||||
p[j] = pp[--k];
|
||||
}
|
||||
cdk_free (pp);
|
||||
}
|
||||
memcpy (frame + n, p, i);
|
||||
cdk_free (p);
|
||||
n += i;
|
||||
frame[n++] = 0;
|
||||
frame[n++] = dek->algo;
|
||||
memcpy (frame + n, dek->key, dek->keylen);
|
||||
n += dek->keylen;
|
||||
frame[n++] = chksum >> 8;
|
||||
frame[n++] = chksum;
|
||||
err = gcry_mpi_scan (&a, GCRYMPI_FMT_USG, frame, nframe, &nframe);
|
||||
cdk_free (frame);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
*r_enc = a;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_dek_decode_pkcs1:
|
||||
* @ret_dek: the decoded DEK object
|
||||
* @esk: the pkcs#1 encoded session key.
|
||||
*
|
||||
* Decode the given multi precision integer in pkcs#1 and
|
||||
* store it into the DEK object.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_dek_decode_pkcs1 (cdk_dek_t * ret_dek, gcry_mpi_t esk)
|
||||
{
|
||||
cdk_dek_t dek;
|
||||
byte frame[MAX_MPI_BYTES + 2 + 1];
|
||||
size_t nframe, n;
|
||||
u16 csum, csum2;
|
||||
gcry_error_t err;
|
||||
|
||||
if (!ret_dek || !esk)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
*ret_dek = NULL; /* reset */
|
||||
nframe = DIM (frame) - 1;
|
||||
err = gcry_mpi_print (GCRYMPI_FMT_USG, frame, nframe, &nframe, esk);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
dek = cdk_salloc (sizeof *dek, 1);
|
||||
if (!dek)
|
||||
return CDK_Out_Of_Core;
|
||||
|
||||
/* Now get the DEK (data encryption key) from the frame
|
||||
*
|
||||
* 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes)
|
||||
*
|
||||
* (gcry_mpi_print already removed the leading zero).
|
||||
*
|
||||
* RND are non-zero randow bytes.
|
||||
* A is the cipher algorithm
|
||||
* DEK is the encryption key (session key) with length k
|
||||
* CSUM
|
||||
*/
|
||||
n = 0;
|
||||
if (frame[n] != 2)
|
||||
{
|
||||
cdk_free (dek);
|
||||
return CDK_Inv_Mode;
|
||||
}
|
||||
for (n++; n < nframe && frame[n]; n++)
|
||||
;
|
||||
n++;
|
||||
dek->keylen = nframe - (n + 1) - 2;
|
||||
dek->algo = frame[n++];
|
||||
if (dek->keylen != gcry_cipher_get_algo_keylen (dek->algo))
|
||||
{
|
||||
_cdk_log_debug ("pkcs1 decode: invalid cipher keylen %d\n",
|
||||
dek->keylen);
|
||||
cdk_free (dek);
|
||||
return CDK_Inv_Algo;
|
||||
}
|
||||
csum = frame[nframe - 2] << 8;
|
||||
csum |= frame[nframe - 1];
|
||||
memcpy (dek->key, frame + n, dek->keylen);
|
||||
csum2 = 0;
|
||||
for (n = 0; n < dek->keylen; n++)
|
||||
csum2 += dek->key[n];
|
||||
if (csum != csum2)
|
||||
{
|
||||
_cdk_log_debug ("pkcs decode: checksum does not match\n");
|
||||
cdk_free (dek);
|
||||
return CDK_Chksum_Error;
|
||||
}
|
||||
*ret_dek = dek;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Encode the given digest into a pkcs#1 compatible format. */
|
||||
cdk_error_t
|
||||
_cdk_digest_encode_pkcs1 (byte ** r_md, size_t * r_mdlen, int pk_algo,
|
||||
const byte * md, int digest_algo, unsigned nbits)
|
||||
{
|
||||
gcry_error_t err;
|
||||
size_t dlen;
|
||||
|
||||
if (!md || !r_md || !r_mdlen)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
dlen = gcry_md_get_algo_dlen (digest_algo);
|
||||
if (!dlen)
|
||||
return CDK_Inv_Algo;
|
||||
if (is_DSA (pk_algo)) /* DSS does not use a special encoding. */
|
||||
{
|
||||
*r_md = cdk_malloc (dlen + 1);
|
||||
if (!*r_md)
|
||||
return CDK_Out_Of_Core;
|
||||
*r_mdlen = dlen;
|
||||
memcpy (*r_md, md, dlen);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte *asn;
|
||||
size_t asnlen;
|
||||
cdk_error_t rc;
|
||||
|
||||
err = gcry_md_get_asnoid (digest_algo, NULL, &asnlen);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
asn = cdk_malloc (asnlen + 1);
|
||||
if (!asn)
|
||||
return CDK_Out_Of_Core;
|
||||
err = gcry_md_get_asnoid (digest_algo, asn, &asnlen);
|
||||
if (err)
|
||||
{
|
||||
cdk_free (asn);
|
||||
return map_gcry_error (err);
|
||||
}
|
||||
rc = do_encode_md (r_md, r_mdlen, md, digest_algo, dlen,
|
||||
nbits, asn, asnlen);
|
||||
cdk_free (asn);
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* FIXME: The prompt should be provided in a more generic way.
|
||||
Like: (keyid, algorithm, [user-id]) */
|
||||
static char *
|
||||
passphrase_prompt (cdk_pkt_seckey_t sk)
|
||||
{
|
||||
u32 keyid = cdk_pk_get_keyid (sk->pk, NULL);
|
||||
int bits = cdk_pk_get_nbits (sk->pk), pk_algo = sk->pubkey_algo;
|
||||
const char *algo = "???", *fmt;
|
||||
char *p;
|
||||
|
||||
if (is_RSA (pk_algo))
|
||||
algo = "RSA";
|
||||
else if (is_ELG (pk_algo))
|
||||
algo = "ELG";
|
||||
else if (is_DSA (pk_algo))
|
||||
algo = "DSA";
|
||||
|
||||
fmt = "%d-bit %s key, ID %08lX\nEnter Passphrase: ";
|
||||
p = cdk_calloc (1, 64 + strlen (fmt) + strlen (algo) + 1);
|
||||
if (!p)
|
||||
return NULL;
|
||||
sprintf (p, fmt, bits, algo, keyid);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
/* Try to unprotect the secret key, if needed, automatically.
|
||||
The passphrase callback is used to get the passphrase directly
|
||||
from the user. */
|
||||
cdk_error_t
|
||||
_cdk_sk_unprotect_auto (cdk_ctx_t hd, cdk_pkt_seckey_t sk)
|
||||
{
|
||||
char *pw, *p;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!sk->is_protected)
|
||||
return 0;
|
||||
|
||||
p = passphrase_prompt (sk);
|
||||
pw = _cdk_passphrase_get (hd, p);
|
||||
cdk_free (p);
|
||||
if (!pw)
|
||||
return CDK_No_Passphrase;
|
||||
|
||||
rc = cdk_sk_unprotect (sk, pw);
|
||||
|
||||
wipemem (pw, strlen (pw));
|
||||
cdk_free (pw);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_dek_extract:
|
||||
* @ret_dek: the raw DEK object
|
||||
* @hd: the session handle
|
||||
* @enc: the public key encrypted packet
|
||||
* @sk: the secret key.
|
||||
*
|
||||
* Try to extract the DEK from the public key encrypted packet.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_dek_extract (cdk_dek_t * ret_dek, cdk_ctx_t hd,
|
||||
cdk_pkt_pubkey_enc_t enc, cdk_pkt_seckey_t sk)
|
||||
{
|
||||
gcry_mpi_t skey = NULL;
|
||||
cdk_dek_t dek;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!enc || !sk || !ret_dek)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
/* FIXME: it is not very elegant that we need the session handle here. */
|
||||
if (sk->is_protected)
|
||||
{
|
||||
rc = _cdk_sk_unprotect_auto (hd, sk);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = cdk_pk_decrypt (sk, enc, &skey);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = cdk_dek_decode_pkcs1 (&dek, skey);
|
||||
gcry_mpi_release (skey);
|
||||
if (rc)
|
||||
{
|
||||
cdk_dek_free (dek);
|
||||
dek = NULL;
|
||||
}
|
||||
*ret_dek = dek;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_dek_new:
|
||||
* @r_dek: the new DEK object
|
||||
*
|
||||
* Create a new DEK object.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_dek_new (cdk_dek_t * r_dek)
|
||||
{
|
||||
cdk_dek_t dek;
|
||||
|
||||
if (!r_dek)
|
||||
return CDK_Inv_Value;
|
||||
*r_dek = NULL;
|
||||
dek = cdk_salloc (sizeof *dek, 1);
|
||||
if (!dek)
|
||||
return CDK_Out_Of_Core;
|
||||
*r_dek = dek;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_dek_set_cipher:
|
||||
* @dek: the DEK object
|
||||
* @algo: the cipher algorithm to use
|
||||
*
|
||||
* Set the cipher for the given DEK object.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_dek_set_cipher (cdk_dek_t dek, int algo)
|
||||
{
|
||||
if (!dek)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (!algo)
|
||||
algo = GCRY_CIPHER_AES128;
|
||||
if (gcry_cipher_test_algo (algo))
|
||||
return CDK_Inv_Algo;
|
||||
dek->algo = algo;
|
||||
dek->keylen = gcry_cipher_get_algo_keylen (dek->algo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cdk_error_t
|
||||
cdk_dek_get_cipher (cdk_dek_t dek, int *r_algo)
|
||||
{
|
||||
if (!dek || !r_algo)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
|
||||
*r_algo = dek->algo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_dek_set_key:
|
||||
* @dek: the DEK object
|
||||
* @key: the random session key
|
||||
* @keylen: the length of the session key.
|
||||
*
|
||||
* Set the random session key for the given DEK object.
|
||||
* If @key and @keylen is NULL (0) a random key will be generated.
|
||||
* In any case, cdk_dek_set_cipher must be called first.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_dek_set_key (cdk_dek_t dek, const byte * key, size_t keylen)
|
||||
{
|
||||
gcry_cipher_hd_t hd;
|
||||
size_t i;
|
||||
|
||||
if (!dek)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
/* The given key must be compatible with the symmetric
|
||||
cipher algorithm set before. */
|
||||
if (keylen > 0 && keylen != dek->keylen)
|
||||
return CDK_Inv_Mode;
|
||||
|
||||
if (!key && !keylen)
|
||||
{
|
||||
gcry_error_t err;
|
||||
|
||||
/* Used to generate a random session key. The extra code is used
|
||||
to detect weak keys, if they are possible at all. */
|
||||
err = gcry_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB,
|
||||
GCRY_CIPHER_ENABLE_SYNC);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM);
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
if (!gcry_cipher_setkey (hd, dek->key, dek->keylen))
|
||||
{
|
||||
gcry_cipher_close (hd);
|
||||
return 0;
|
||||
}
|
||||
gcry_randomize (dek->key, dek->keylen, GCRY_STRONG_RANDOM);
|
||||
}
|
||||
gcry_cipher_close (hd);
|
||||
return CDK_Weak_Key;
|
||||
}
|
||||
|
||||
memcpy (dek->key, key, dek->keylen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_dek_set_mdc_flag:
|
||||
* @dek: the DEK object
|
||||
* @val: value to enable or disable the use
|
||||
*
|
||||
* Enable or disable the MDC flag for the given DEK object.
|
||||
**/
|
||||
void
|
||||
cdk_dek_set_mdc_flag (cdk_dek_t dek, int val)
|
||||
{
|
||||
if (dek)
|
||||
dek->use_mdc = val;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
cdk_dek_get_mdc_flag (cdk_dek_t dek)
|
||||
{
|
||||
if (!dek)
|
||||
return 0;
|
||||
return dek->use_mdc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_dek_free:
|
||||
* @dek: the DEK object
|
||||
*
|
||||
* Release the DEK object.
|
||||
**/
|
||||
void
|
||||
cdk_dek_free (cdk_dek_t dek)
|
||||
{
|
||||
if (!dek)
|
||||
return;
|
||||
|
||||
/* Make sure sentensive data is overwritten. */
|
||||
wipemem (dek->key, sizeof (dek->key));
|
||||
cdk_free (dek);
|
||||
}
|
||||
|
||||
|
||||
/* Hash the passphrase to produce the a DEK.
|
||||
If create is set, a random salt will be generated. */
|
||||
static cdk_error_t
|
||||
hash_passphrase (cdk_dek_t dek, const char *pw, cdk_s2k_t s2k, int create)
|
||||
{
|
||||
gcry_md_hd_t md;
|
||||
byte zero[1] = { 0x00 };
|
||||
int pass, i;
|
||||
int used = 0, pwlen;
|
||||
gcry_error_t err;
|
||||
|
||||
if (!dek || !pw || !s2k)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (!s2k->hash_algo)
|
||||
s2k->hash_algo = GCRY_MD_SHA1;
|
||||
pwlen = strlen (pw);
|
||||
|
||||
dek->keylen = gcry_cipher_get_algo_keylen (dek->algo);
|
||||
err = gcry_md_open (&md, s2k->hash_algo, 0);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
|
||||
for (pass = 0; used < dek->keylen; pass++)
|
||||
{
|
||||
if (pass)
|
||||
{
|
||||
gcry_md_reset (md);
|
||||
for (i = 0; i < pass; i++) /* preset the hash context */
|
||||
gcry_md_write (md, zero, 1);
|
||||
}
|
||||
if (s2k->mode == CDK_S2K_SALTED || s2k->mode == CDK_S2K_ITERSALTED)
|
||||
{
|
||||
int len2 = pwlen + 8;
|
||||
u32 count = len2;
|
||||
if (create && !pass)
|
||||
{
|
||||
gcry_randomize (s2k->salt, 8, GCRY_STRONG_RANDOM);
|
||||
if (s2k->mode == 3)
|
||||
s2k->count = 96; /* 65536 iterations */
|
||||
}
|
||||
if (s2k->mode == 3)
|
||||
{
|
||||
count = (16ul + (s2k->count & 15)) << ((s2k->count >> 4) + 6);
|
||||
if (count < len2)
|
||||
count = len2;
|
||||
}
|
||||
/* a little bit complicated because we need a ulong for count */
|
||||
while (count > len2)
|
||||
{ /* maybe iterated+salted */
|
||||
gcry_md_write (md, s2k->salt, 8);
|
||||
gcry_md_write (md, pw, pwlen);
|
||||
count -= len2;
|
||||
}
|
||||
if (count < 8)
|
||||
gcry_md_write (md, s2k->salt, count);
|
||||
else
|
||||
{
|
||||
gcry_md_write (md, s2k->salt, 8);
|
||||
count -= 8;
|
||||
gcry_md_write (md, pw, count);
|
||||
}
|
||||
}
|
||||
else
|
||||
gcry_md_write (md, pw, pwlen);
|
||||
gcry_md_final (md);
|
||||
i = gcry_md_get_algo_dlen (s2k->hash_algo);
|
||||
if (i > dek->keylen - used)
|
||||
i = dek->keylen - used;
|
||||
memcpy (dek->key + used, gcry_md_read (md, s2k->hash_algo), i);
|
||||
used += i;
|
||||
}
|
||||
gcry_md_close (md);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_dek_from_passphrase:
|
||||
* @ret_dek: the new DEK.
|
||||
* @cipher_algo: symmetric key algorithm to use
|
||||
* @s2k: the S2K to use
|
||||
* @rndsalt: 1=create random salt
|
||||
* @pw: the passphrase.
|
||||
*
|
||||
* Transform a passphrase into a DEK object.
|
||||
*/
|
||||
cdk_error_t
|
||||
cdk_dek_from_passphrase (cdk_dek_t * ret_dek, int cipher_algo, cdk_s2k_t s2k,
|
||||
int rndsalt, const char *pw)
|
||||
{
|
||||
cdk_dek_t dek;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!ret_dek)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
*ret_dek = NULL;
|
||||
rc = cdk_dek_new (&dek);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = cdk_dek_set_cipher (dek, cipher_algo);
|
||||
if (!rc)
|
||||
rc = hash_passphrase (dek, pw, s2k, rndsalt);
|
||||
if (rc)
|
||||
{
|
||||
cdk_dek_free (dek);
|
||||
return rc;
|
||||
}
|
||||
|
||||
*ret_dek = dek;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_s2k_new:
|
||||
* @ret_s2k: output for the new S2K object
|
||||
* @mode: the S2K mode (simple, salted, iter+salted)
|
||||
* @digest_algo: the hash algorithm
|
||||
* @salt: random salt
|
||||
*
|
||||
* Create a new S2K object with the given parameter.
|
||||
* The @salt parameter must be always 8 octets.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_s2k_new (cdk_s2k_t * ret_s2k, int mode, int digest_algo,
|
||||
const byte * salt)
|
||||
{
|
||||
cdk_s2k_t s2k;
|
||||
|
||||
if (!ret_s2k)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (mode != 0x00 && mode != 0x01 && mode != 0x03)
|
||||
return CDK_Inv_Mode;
|
||||
|
||||
if (gcry_md_test_algo (digest_algo))
|
||||
return CDK_Inv_Algo;
|
||||
|
||||
s2k = cdk_calloc (1, sizeof *s2k);
|
||||
if (!s2k)
|
||||
return CDK_Out_Of_Core;
|
||||
s2k->mode = mode;
|
||||
s2k->hash_algo = digest_algo;
|
||||
if (salt)
|
||||
memcpy (s2k->salt, salt, 8);
|
||||
*ret_s2k = s2k;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_s2k_free:
|
||||
* @s2k: the S2K object
|
||||
*
|
||||
* Release the given S2K object.
|
||||
**/
|
||||
void
|
||||
cdk_s2k_free (cdk_s2k_t s2k)
|
||||
{
|
||||
cdk_free (s2k);
|
||||
}
|
||||
|
||||
|
||||
/* Make a copy of the source s2k into R_DST. */
|
||||
cdk_error_t
|
||||
_cdk_s2k_copy (cdk_s2k_t * r_dst, cdk_s2k_t src)
|
||||
{
|
||||
cdk_s2k_t dst;
|
||||
cdk_error_t err;
|
||||
|
||||
err = cdk_s2k_new (&dst, src->mode, src->hash_algo, src->salt);
|
||||
if (err)
|
||||
return err;
|
||||
dst->count = src->count;
|
||||
*r_dst = dst;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,489 +0,0 @@
|
||||
/* sig-check.c - Check signatures
|
||||
* Copyright (C) 2001, 2002, 2003, 2007 Timo Schulz
|
||||
* Copyright (C) 1998-2002 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <gcrypt.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "opencdk.h"
|
||||
#include "main.h"
|
||||
#include "packet.h"
|
||||
|
||||
|
||||
/* Hash all multi precision integers of the key PK with the given
|
||||
message digest context MD. */
|
||||
static int
|
||||
hash_mpibuf (cdk_pubkey_t pk, gcry_md_hd_t md, int usefpr)
|
||||
{
|
||||
byte buf[MAX_MPI_BYTES]; /* FIXME: do not use hardcoded length. */
|
||||
size_t nbytes;
|
||||
size_t i, npkey;
|
||||
gcry_error_t err;
|
||||
|
||||
/* We have to differ between two modes for v3 keys. To form the
|
||||
fingerprint, we hash the MPI values without the length prefix.
|
||||
But if we calculate the hash for verifying/signing we use all data. */
|
||||
npkey = cdk_pk_get_npkey (pk->pubkey_algo);
|
||||
for (i = 0; i < npkey; i++)
|
||||
{
|
||||
err = gcry_mpi_print (GCRYMPI_FMT_PGP, buf, MAX_MPI_BYTES,
|
||||
&nbytes, pk->mpi[i]);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
if (!usefpr || pk->version == 4)
|
||||
gcry_md_write (md, buf, nbytes);
|
||||
else /* without the prefix. */
|
||||
gcry_md_write (md, buf + 2, nbytes - 2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Hash an entire public key PK with the given message digest context
|
||||
MD. The @usefpr param is only valid for version 3 keys because of
|
||||
the different way to calculate the fingerprint. */
|
||||
cdk_error_t
|
||||
_cdk_hash_pubkey (cdk_pubkey_t pk, gcry_md_hd_t md, int usefpr)
|
||||
{
|
||||
byte buf[12];
|
||||
size_t i, n, npkey;
|
||||
|
||||
if (!pk || !md)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (usefpr && pk->version < 4 && is_RSA (pk->pubkey_algo))
|
||||
return hash_mpibuf (pk, md, 1);
|
||||
|
||||
/* The version 4 public key packet does not have the 2 octets for
|
||||
the expiration date. */
|
||||
n = pk->version < 4 ? 8 : 6;
|
||||
npkey = cdk_pk_get_npkey (pk->pubkey_algo);
|
||||
for (i = 0; i < npkey; i++)
|
||||
n = n + (gcry_mpi_get_nbits (pk->mpi[i]) + 7) / 8 + 2;
|
||||
|
||||
i = 0;
|
||||
buf[i++] = 0x99;
|
||||
buf[i++] = n >> 8;
|
||||
buf[i++] = n >> 0;
|
||||
buf[i++] = pk->version;
|
||||
buf[i++] = pk->timestamp >> 24;
|
||||
buf[i++] = pk->timestamp >> 16;
|
||||
buf[i++] = pk->timestamp >> 8;
|
||||
buf[i++] = pk->timestamp >> 0;
|
||||
|
||||
if (pk->version < 4)
|
||||
{
|
||||
u16 a = 0;
|
||||
|
||||
/* Convert the expiration date into days. */
|
||||
if (pk->expiredate)
|
||||
a = (u16) ((pk->expiredate - pk->timestamp) / 86400L);
|
||||
buf[i++] = a >> 8;
|
||||
buf[i++] = a;
|
||||
}
|
||||
buf[i++] = pk->pubkey_algo;
|
||||
gcry_md_write (md, buf, i);
|
||||
return hash_mpibuf (pk, md, 0);
|
||||
}
|
||||
|
||||
|
||||
/* Hash the user ID @uid with the given message digest @md.
|
||||
Use openpgp mode if @is_v4 is 1. */
|
||||
cdk_error_t
|
||||
_cdk_hash_userid (cdk_pkt_userid_t uid, int is_v4, gcry_md_hd_t md)
|
||||
{
|
||||
const byte *data;
|
||||
byte buf[5];
|
||||
u32 dlen;
|
||||
|
||||
if (!uid || !md)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (!is_v4)
|
||||
{
|
||||
gcry_md_write (md, (byte *) uid->name, uid->len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dlen = uid->attrib_img ? uid->attrib_len : uid->len;
|
||||
data = uid->attrib_img ? uid->attrib_img : (byte *) uid->name;
|
||||
buf[0] = uid->attrib_img ? 0xD1 : 0xB4;
|
||||
buf[1] = dlen >> 24;
|
||||
buf[2] = dlen >> 16;
|
||||
buf[3] = dlen >> 8;
|
||||
buf[4] = dlen >> 0;
|
||||
gcry_md_write (md, buf, 5);
|
||||
gcry_md_write (md, data, dlen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Hash all parts of the signature which are needed to derive
|
||||
the correct message digest to verify the sig. */
|
||||
cdk_error_t
|
||||
_cdk_hash_sig_data (cdk_pkt_signature_t sig, gcry_md_hd_t md)
|
||||
{
|
||||
byte buf[4];
|
||||
|
||||
if (!sig || !md)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (sig->version == 4)
|
||||
gcry_md_putc (md, sig->version);
|
||||
gcry_md_putc (md, sig->sig_class);
|
||||
if (sig->version < 4)
|
||||
{
|
||||
buf[0] = sig->timestamp >> 24;
|
||||
buf[1] = sig->timestamp >> 16;
|
||||
buf[2] = sig->timestamp >> 8;
|
||||
buf[3] = sig->timestamp >> 0;
|
||||
gcry_md_write (md, buf, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t n;
|
||||
|
||||
gcry_md_putc (md, sig->pubkey_algo);
|
||||
gcry_md_putc (md, sig->digest_algo);
|
||||
if (sig->hashed != NULL)
|
||||
{
|
||||
byte *p = _cdk_subpkt_get_array (sig->hashed, 0, &n);
|
||||
assert (p != NULL);
|
||||
buf[0] = n >> 8;
|
||||
buf[1] = n >> 0;
|
||||
gcry_md_write (md, buf, 2);
|
||||
gcry_md_write (md, p, n);
|
||||
cdk_free (p);
|
||||
sig->hashed_size = n;
|
||||
n = sig->hashed_size + 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
gcry_md_putc (md, 0x00);
|
||||
gcry_md_putc (md, 0x00);
|
||||
n = 6;
|
||||
}
|
||||
gcry_md_putc (md, sig->version);
|
||||
gcry_md_putc (md, 0xFF);
|
||||
buf[0] = n >> 24;
|
||||
buf[1] = n >> 16;
|
||||
buf[2] = n >> 8;
|
||||
buf[3] = n >> 0;
|
||||
gcry_md_write (md, buf, 4);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Cache the signature result and store it inside the sig. */
|
||||
static void
|
||||
cache_sig_result (cdk_pkt_signature_t sig, int res)
|
||||
{
|
||||
sig->flags.checked = 0;
|
||||
sig->flags.valid = 0;
|
||||
if (res == 0)
|
||||
{
|
||||
sig->flags.checked = 1;
|
||||
sig->flags.valid = 1;
|
||||
}
|
||||
else if (res == CDK_Bad_Sig)
|
||||
{
|
||||
sig->flags.checked = 1;
|
||||
sig->flags.valid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Check the given signature @sig with the public key @pk.
|
||||
Use the digest handle @digest. */
|
||||
cdk_error_t
|
||||
_cdk_sig_check (cdk_pubkey_t pk, cdk_pkt_signature_t sig,
|
||||
gcry_md_hd_t digest, int *r_expired)
|
||||
{
|
||||
cdk_error_t rc;
|
||||
byte md[MAX_DIGEST_LEN];
|
||||
time_t cur_time = (u32) time (NULL);
|
||||
|
||||
if (!pk || !sig || !digest)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (sig->flags.checked)
|
||||
return sig->flags.valid ? 0 : CDK_Bad_Sig;
|
||||
if (!KEY_CAN_SIGN (pk->pubkey_algo))
|
||||
return CDK_Inv_Algo;
|
||||
if (pk->timestamp > sig->timestamp || pk->timestamp > cur_time)
|
||||
return CDK_Time_Conflict;
|
||||
|
||||
if (r_expired && pk->expiredate
|
||||
&& (pk->expiredate + pk->timestamp) > cur_time)
|
||||
*r_expired = 1;
|
||||
|
||||
_cdk_hash_sig_data (sig, digest);
|
||||
gcry_md_final (digest);
|
||||
memcpy (md, gcry_md_read (digest, sig->digest_algo),
|
||||
gcry_md_get_algo_dlen (sig->digest_algo));
|
||||
|
||||
if (md[0] != sig->digest_start[0] || md[1] != sig->digest_start[1])
|
||||
return CDK_Chksum_Error;
|
||||
|
||||
rc = cdk_pk_verify (pk, sig, md);
|
||||
cache_sig_result (sig, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Check the given key signature.
|
||||
@knode is the key node and @snode the signature node. */
|
||||
cdk_error_t
|
||||
_cdk_pk_check_sig (cdk_keydb_hd_t keydb,
|
||||
cdk_kbnode_t knode, cdk_kbnode_t snode, int *is_selfsig)
|
||||
{
|
||||
gcry_md_hd_t md;
|
||||
gcry_error_t err;
|
||||
cdk_pubkey_t pk;
|
||||
cdk_pkt_signature_t sig;
|
||||
cdk_kbnode_t node;
|
||||
cdk_error_t rc = 0;
|
||||
int is_expired;
|
||||
|
||||
if (!knode || !snode)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (is_selfsig)
|
||||
*is_selfsig = 0;
|
||||
if (knode->pkt->pkttype != CDK_PKT_PUBLIC_KEY ||
|
||||
snode->pkt->pkttype != CDK_PKT_SIGNATURE)
|
||||
return CDK_Inv_Value;
|
||||
pk = knode->pkt->pkt.public_key;
|
||||
sig = snode->pkt->pkt.signature;
|
||||
|
||||
err = gcry_md_open (&md, sig->digest_algo, 0);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
|
||||
is_expired = 0;
|
||||
if (sig->sig_class == 0x20)
|
||||
{ /* key revocation */
|
||||
cdk_kbnode_hash (knode, md, 0, 0, 0);
|
||||
rc = _cdk_sig_check (pk, sig, md, &is_expired);
|
||||
}
|
||||
else if (sig->sig_class == 0x28)
|
||||
{ /* subkey revocation */
|
||||
node = cdk_kbnode_find_prev (knode, snode, CDK_PKT_PUBLIC_SUBKEY);
|
||||
if (!node)
|
||||
{ /* no subkey for subkey revocation packet */
|
||||
rc = CDK_Error_No_Key;
|
||||
goto fail;
|
||||
}
|
||||
cdk_kbnode_hash (knode, md, 0, 0, 0);
|
||||
cdk_kbnode_hash (node, md, 0, 0, 0);
|
||||
rc = _cdk_sig_check (pk, sig, md, &is_expired);
|
||||
}
|
||||
else if (sig->sig_class == 0x18 || sig->sig_class == 0x19)
|
||||
{ /* primary/secondary key binding */
|
||||
node = cdk_kbnode_find_prev (knode, snode, CDK_PKT_PUBLIC_SUBKEY);
|
||||
if (!node)
|
||||
{ /* no subkey for subkey binding packet */
|
||||
rc = CDK_Error_No_Key;
|
||||
goto fail;
|
||||
}
|
||||
cdk_kbnode_hash (knode, md, 0, 0, 0);
|
||||
cdk_kbnode_hash (node, md, 0, 0, 0);
|
||||
rc = _cdk_sig_check (pk, sig, md, &is_expired);
|
||||
}
|
||||
else if (sig->sig_class == 0x1F)
|
||||
{ /* direct key signature */
|
||||
cdk_kbnode_hash (knode, md, 0, 0, 0);
|
||||
rc = _cdk_sig_check (pk, sig, md, &is_expired);
|
||||
}
|
||||
else
|
||||
{ /* all other classes */
|
||||
node = cdk_kbnode_find_prev (knode, snode, CDK_PKT_USER_ID);
|
||||
if (!node)
|
||||
{ /* no user ID for key signature packet */
|
||||
rc = CDK_Error_No_Key;
|
||||
goto fail;
|
||||
}
|
||||
cdk_kbnode_hash (knode, md, 0, 0, 0);
|
||||
cdk_kbnode_hash (node, md, sig->version == 4, 0, 0);
|
||||
if (pk->keyid[0] == sig->keyid[0] && pk->keyid[1] == sig->keyid[1])
|
||||
{
|
||||
rc = _cdk_sig_check (pk, sig, md, &is_expired);
|
||||
if (is_selfsig)
|
||||
*is_selfsig = 1;
|
||||
}
|
||||
else if (keydb != NULL)
|
||||
{
|
||||
cdk_pubkey_t sig_pk;
|
||||
|
||||
rc = cdk_keydb_get_pk (keydb, sig->keyid, &sig_pk);
|
||||
if (!rc)
|
||||
rc = _cdk_sig_check (sig_pk, sig, md, &is_expired);
|
||||
cdk_pk_release (sig_pk);
|
||||
}
|
||||
}
|
||||
fail:
|
||||
gcry_md_close (md);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pk_check_sigs:
|
||||
* @key: the public key
|
||||
* @hd: an optinal key database handle
|
||||
* @r_status: variable to store the status of the key
|
||||
*
|
||||
* Check all signatures. When no key is available for checking, the
|
||||
* sigstat is marked as 'NOKEY'. The @r_status contains the key flags
|
||||
* which are or-ed or zero when there are no flags.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_pk_check_sigs (cdk_kbnode_t key, cdk_keydb_hd_t keydb, int *r_status)
|
||||
{
|
||||
cdk_pkt_signature_t sig;
|
||||
cdk_kbnode_t node;
|
||||
cdk_error_t rc;
|
||||
u32 keyid;
|
||||
int key_status, is_selfsig = 0;
|
||||
int no_signer, n_sigs = 0;
|
||||
|
||||
if (!key || !r_status)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
*r_status = 0;
|
||||
node = cdk_kbnode_find (key, CDK_PKT_PUBLIC_KEY);
|
||||
if (!node)
|
||||
return CDK_Error_No_Key;
|
||||
|
||||
key_status = 0;
|
||||
/* Continue with the signature check but adjust the
|
||||
key status flags accordingly. */
|
||||
if (node->pkt->pkt.public_key->is_revoked)
|
||||
key_status |= CDK_KEY_REVOKED;
|
||||
if (node->pkt->pkt.public_key->has_expired)
|
||||
key_status |= CDK_KEY_EXPIRED;
|
||||
|
||||
rc = 0;
|
||||
no_signer = 0;
|
||||
keyid = cdk_pk_get_keyid (node->pkt->pkt.public_key, NULL);
|
||||
for (node = key; node; node = node->next)
|
||||
{
|
||||
if (node->pkt->pkttype != CDK_PKT_SIGNATURE)
|
||||
continue;
|
||||
sig = node->pkt->pkt.signature;
|
||||
rc = _cdk_pk_check_sig (keydb, key, node, &is_selfsig);
|
||||
if (IS_UID_SIG (sig))
|
||||
{
|
||||
if (is_selfsig == 0)
|
||||
n_sigs++;
|
||||
}
|
||||
if (rc && IS_UID_SIG (sig) && rc == CDK_Error_No_Key)
|
||||
{
|
||||
/* We do not consider it a problem when the signing key
|
||||
is not avaiable. We just mark the signature accordingly
|
||||
and contine. */
|
||||
sig->flags.missing_key = 1;
|
||||
no_signer++;
|
||||
}
|
||||
else if (rc && rc != CDK_Error_No_Key)
|
||||
{
|
||||
/* It might be possible that a single signature has been
|
||||
corrupted, thus we do not consider it a problem when
|
||||
one ore more signatures are bad. But at least the self
|
||||
signature has to be valid. */
|
||||
if (is_selfsig)
|
||||
{
|
||||
key_status |= CDK_KEY_INVALID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_cdk_log_debug ("signature %s: signer %08lX keyid %08lX\n",
|
||||
rc == CDK_Bad_Sig ? "BAD" : "good", sig->keyid[1],
|
||||
keyid);
|
||||
}
|
||||
|
||||
if (n_sigs == no_signer)
|
||||
key_status |= CDK_KEY_NOSIGNER;
|
||||
*r_status = key_status;
|
||||
if (rc == CDK_Error_No_Key)
|
||||
rc = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pk_check_self_sig:
|
||||
* @key: the key node
|
||||
* @r_status: output the status of the key.
|
||||
*
|
||||
* A convenient function to make sure the key is valid.
|
||||
* Valid means the self signature is ok.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_pk_check_self_sig (cdk_kbnode_t key, int *r_status)
|
||||
{
|
||||
cdk_pkt_signature_t sig;
|
||||
cdk_kbnode_t node;
|
||||
cdk_error_t rc;
|
||||
u32 keyid[2], sigid[2];
|
||||
int is_selfsig, sig_ok;
|
||||
|
||||
if (!key || !r_status)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
node = cdk_kbnode_find (key, CDK_PKT_PUBLIC_KEY);
|
||||
if (!node)
|
||||
return CDK_Error_No_Key;
|
||||
/* FIXME: we should set expire/revoke here also but callers
|
||||
expect CDK_KEY_VALID=0 if the key is okay. */
|
||||
cdk_pk_get_keyid (key->pkt->pkt.public_key, keyid);
|
||||
sig_ok = 0;
|
||||
for (node = key; node; node = node->next)
|
||||
{
|
||||
if (node->pkt->pkttype != CDK_PKT_SIGNATURE)
|
||||
continue;
|
||||
sig = node->pkt->pkt.signature;
|
||||
if (!IS_UID_SIG (sig))
|
||||
continue;
|
||||
cdk_sig_get_keyid (sig, sigid);
|
||||
if (sigid[0] != keyid[0] || sigid[1] != keyid[1])
|
||||
continue;
|
||||
/* FIXME: Now we check all self signatures. */
|
||||
rc = _cdk_pk_check_sig (NULL, key, node, &is_selfsig);
|
||||
if (rc)
|
||||
{
|
||||
*r_status = CDK_KEY_INVALID;
|
||||
return rc;
|
||||
}
|
||||
else /* For each valid self sig we increase this counter. */
|
||||
sig_ok++;
|
||||
}
|
||||
|
||||
/* A key without a self signature is not valid. */
|
||||
if (!sig_ok)
|
||||
{
|
||||
*r_status = CDK_KEY_INVALID;
|
||||
return CDK_General_Error;
|
||||
}
|
||||
/* No flags indicate a valid key. */
|
||||
*r_status = CDK_KEY_VALID;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,1446 +0,0 @@
|
||||
/* stream.c - The stream implementation
|
||||
* Copyright (C) 2002, 2003, 2007 Timo Schulz
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "opencdk.h"
|
||||
#include "main.h"
|
||||
#include "filters.h"
|
||||
#include "stream.h"
|
||||
#include "types.h"
|
||||
|
||||
/* This is the maximal amount of bytes we map. */
|
||||
#define MAX_MAP_SIZE 16777216
|
||||
|
||||
static int stream_flush (cdk_stream_t s);
|
||||
static int stream_filter_write (cdk_stream_t s);
|
||||
static int stream_cache_flush (cdk_stream_t s, FILE * fp);
|
||||
|
||||
/* Customized tmpfile() version from misc.c */
|
||||
FILE *my_tmpfile (void);
|
||||
|
||||
|
||||
/* FIXME: The read/write/putc/getc function cannot directly
|
||||
return an error code. It is stored in an error variable
|
||||
inside the string. Right now there is no code to
|
||||
return the error code or to reset it. */
|
||||
|
||||
/**
|
||||
* cdk_stream_open:
|
||||
* @file: The file to open
|
||||
* @ret_s: The new STREAM object
|
||||
*
|
||||
* Create a new stream based on an existing file. The stream is
|
||||
* opened in read-only mode.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_stream_open (const char *file, cdk_stream_t * ret_s)
|
||||
{
|
||||
return _cdk_stream_open_mode (file, "rb", ret_s);
|
||||
}
|
||||
|
||||
|
||||
/* Helper function to allow to open a stream in different modes. */
|
||||
cdk_error_t
|
||||
_cdk_stream_open_mode (const char *file, const char *mode,
|
||||
cdk_stream_t * ret_s)
|
||||
{
|
||||
cdk_stream_t s;
|
||||
|
||||
if (!file || !ret_s)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
_cdk_log_debug ("open stream `%s'\n", file);
|
||||
*ret_s = NULL;
|
||||
s = cdk_calloc (1, sizeof *s);
|
||||
if (!s)
|
||||
return CDK_Out_Of_Core;
|
||||
s->fname = cdk_strdup (file);
|
||||
if (!s->fname)
|
||||
{
|
||||
cdk_free (s);
|
||||
return CDK_Out_Of_Core;
|
||||
}
|
||||
s->fp = fopen (file, mode);
|
||||
if (!s->fp)
|
||||
{
|
||||
cdk_free (s->fname);
|
||||
cdk_free (s);
|
||||
return CDK_File_Error;
|
||||
}
|
||||
_cdk_log_debug ("open stream fd=%d\n", fileno (s->fp));
|
||||
s->flags.write = 0;
|
||||
*ret_s = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_new_from_cbs:
|
||||
* @cbs: the callback context with all user callback functions
|
||||
* @opa: opaque handle which is passed to all callbacks.
|
||||
* @ret_s: the allocated stream
|
||||
*
|
||||
* This function creates a stream which uses user callback
|
||||
* for the core operations (open, close, read, write, seek).
|
||||
*/
|
||||
cdk_error_t
|
||||
cdk_stream_new_from_cbs (cdk_stream_cbs_t cbs, void *opa,
|
||||
cdk_stream_t * ret_s)
|
||||
{
|
||||
cdk_stream_t s;
|
||||
|
||||
if (!cbs || !opa || !ret_s)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
*ret_s = NULL;
|
||||
s = cdk_calloc (1, sizeof *s);
|
||||
if (!s)
|
||||
return CDK_Out_Of_Core;
|
||||
|
||||
s->cbs.read = cbs->read;
|
||||
s->cbs.write = cbs->write;
|
||||
s->cbs.seek = cbs->seek;
|
||||
s->cbs.release = cbs->release;
|
||||
s->cbs.open = cbs->open;
|
||||
s->cbs_hd = opa;
|
||||
*ret_s = s;
|
||||
|
||||
/* If there is a user callback for open, we need to call it
|
||||
here because read/write expects an open stream. */
|
||||
if (s->cbs.open)
|
||||
return s->cbs.open (s->cbs_hd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_new: Create a new stream into into the given file.
|
||||
* @file: The name of the new file
|
||||
* @ret_s: The new STREAM object
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_stream_new (const char *file, cdk_stream_t * ret_s)
|
||||
{
|
||||
cdk_stream_t s;
|
||||
|
||||
if (!ret_s)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
_cdk_log_debug ("new stream `%s'\n", file ? file : "[temp]");
|
||||
*ret_s = NULL;
|
||||
s = cdk_calloc (1, sizeof *s);
|
||||
if (!s)
|
||||
return CDK_Out_Of_Core;
|
||||
s->flags.write = 1;
|
||||
if (!file)
|
||||
s->flags.temp = 1;
|
||||
else
|
||||
{
|
||||
s->fname = cdk_strdup (file);
|
||||
if (!s->fname)
|
||||
{
|
||||
cdk_free (s);
|
||||
return CDK_Out_Of_Core;
|
||||
}
|
||||
}
|
||||
s->fp = my_tmpfile ();
|
||||
if (!s->fp)
|
||||
{
|
||||
cdk_free (s->fname);
|
||||
cdk_free (s);
|
||||
return CDK_File_Error;
|
||||
}
|
||||
_cdk_log_debug ("new stream fd=%d\n", fileno (s->fp));
|
||||
*ret_s = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_create: create a new stream.
|
||||
* @file: the filename
|
||||
* @ret_s: the object
|
||||
*
|
||||
* The difference to cdk_stream_new is, that no filtering can be used with
|
||||
* this kind of stream and everything is written directly to the stream.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_stream_create (const char *file, cdk_stream_t * ret_s)
|
||||
{
|
||||
cdk_stream_t s;
|
||||
|
||||
if (!file || !ret_s)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
_cdk_log_debug ("create stream `%s'\n", file);
|
||||
*ret_s = NULL;
|
||||
s = cdk_calloc (1, sizeof *s);
|
||||
if (!s)
|
||||
return CDK_Out_Of_Core;
|
||||
s->flags.write = 1;
|
||||
s->flags.filtrated = 1;
|
||||
s->fname = cdk_strdup (file);
|
||||
if (!s->fname)
|
||||
{
|
||||
cdk_free (s);
|
||||
return CDK_Out_Of_Core;
|
||||
}
|
||||
s->fp = fopen (file, "w+b");
|
||||
if (!s->fp)
|
||||
{
|
||||
cdk_free (s->fname);
|
||||
cdk_free (s);
|
||||
return CDK_File_Error;
|
||||
}
|
||||
_cdk_log_debug ("stream create fd=%d\n", fileno (s->fp));
|
||||
*ret_s = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_tmp_new:
|
||||
* @r_out: the new temp stream.
|
||||
*
|
||||
* Allocate a new tempory stream which is not associated with a file.
|
||||
*/
|
||||
cdk_error_t
|
||||
cdk_stream_tmp_new (cdk_stream_t * r_out)
|
||||
{
|
||||
return cdk_stream_new (NULL, r_out);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_tmp_from_mem:
|
||||
* @buf: the buffer which shall be written to the temp stream.
|
||||
* @buflen: how large the buffer is
|
||||
* @r_out: the new stream with the given contents.
|
||||
*
|
||||
* Create a new tempory stream with the given contests.
|
||||
*/
|
||||
cdk_error_t
|
||||
cdk_stream_tmp_from_mem (const void *buf, size_t buflen, cdk_stream_t * r_out)
|
||||
{
|
||||
cdk_stream_t s;
|
||||
cdk_error_t rc;
|
||||
int nwritten;
|
||||
|
||||
*r_out = NULL;
|
||||
rc = cdk_stream_tmp_new (&s);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
nwritten = cdk_stream_write (s, buf, buflen);
|
||||
if (nwritten == EOF)
|
||||
{
|
||||
cdk_stream_close (s);
|
||||
return s->error;
|
||||
}
|
||||
cdk_stream_seek (s, 0);
|
||||
*r_out = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
_cdk_stream_fpopen (FILE * fp, unsigned write_mode, cdk_stream_t * ret_out)
|
||||
{
|
||||
cdk_stream_t s;
|
||||
|
||||
*ret_out = NULL;
|
||||
s = cdk_calloc (1, sizeof *s);
|
||||
if (!s)
|
||||
return CDK_Out_Of_Core;
|
||||
|
||||
_cdk_log_debug ("stream ref fd=%d\n", fileno (fp));
|
||||
s->fp = fp;
|
||||
s->fp_ref = 1;
|
||||
s->flags.filtrated = 1;
|
||||
s->flags.write = write_mode;
|
||||
|
||||
*ret_out = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
_cdk_stream_append (const char *file, cdk_stream_t * ret_s)
|
||||
{
|
||||
cdk_stream_t s;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!ret_s)
|
||||
return CDK_Inv_Value;
|
||||
*ret_s = NULL;
|
||||
|
||||
rc = _cdk_stream_open_mode (file, "a+b", &s);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* In the append mode, we need to write to the flag. */
|
||||
s->flags.write = 1;
|
||||
*ret_s = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_is_compressed:
|
||||
* @s: the stream
|
||||
*
|
||||
* Return 0 if the stream is uncompressed, otherwise the
|
||||
* compression algorithm.
|
||||
*/
|
||||
int
|
||||
cdk_stream_is_compressed (cdk_stream_t s)
|
||||
{
|
||||
if (!s)
|
||||
return 0;
|
||||
return s->flags.compressed;
|
||||
}
|
||||
|
||||
void
|
||||
_cdk_stream_set_compress_algo (cdk_stream_t s, int algo)
|
||||
{
|
||||
if (!s)
|
||||
return;
|
||||
s->flags.compressed = algo;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
cdk_stream_flush (cdk_stream_t s)
|
||||
{
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!s)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
/* The user callback does not support flush */
|
||||
if (s->cbs_hd)
|
||||
return 0;
|
||||
|
||||
/* For read-only streams, no flush is needed. */
|
||||
if (!s->flags.write)
|
||||
return 0;
|
||||
|
||||
if (!s->flags.filtrated)
|
||||
{
|
||||
if (!cdk_stream_get_length (s))
|
||||
return 0;
|
||||
rc = cdk_stream_seek (s, 0);
|
||||
if (!rc)
|
||||
rc = stream_flush (s);
|
||||
if (!rc)
|
||||
rc = stream_filter_write (s);
|
||||
s->flags.filtrated = 1;
|
||||
if (rc)
|
||||
{
|
||||
s->error = rc;
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
cdk_stream_tmp_set_mode (cdk_stream_t s, int val)
|
||||
{
|
||||
if (s && s->flags.temp)
|
||||
s->fmode = val;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_close: Close a stream and flush all buffers.
|
||||
* @s: The STREAM object.
|
||||
*
|
||||
* This function work different for read or write streams. When the
|
||||
* stream is for reading, the filtering is already done and we can
|
||||
* simply close the file and all buffers.
|
||||
* But for the case it's a write stream, we need to apply all registered
|
||||
* filters now. The file is closed in the filter function and not here.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_stream_close (cdk_stream_t s)
|
||||
{
|
||||
struct stream_filter_s *f, *f2;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!s)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
_cdk_log_debug ("close stream ref=%d `%s'\n",
|
||||
s->fp_ref, s->fname ? s->fname : "[temp]");
|
||||
|
||||
/* In the user callback mode, we call the release cb if possible
|
||||
and just free the stream. */
|
||||
if (s->cbs_hd)
|
||||
{
|
||||
if (s->cbs.release)
|
||||
rc = s->cbs.release (s->cbs_hd);
|
||||
else
|
||||
rc = 0;
|
||||
cdk_free (s);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
rc = 0;
|
||||
if (!s->flags.filtrated && !s->error)
|
||||
rc = cdk_stream_flush (s);
|
||||
if (!s->fp_ref && (s->fname || s->flags.temp))
|
||||
{
|
||||
int err;
|
||||
|
||||
_cdk_log_debug ("close stream fd=%d\n", fileno (s->fp));
|
||||
err = fclose (s->fp);
|
||||
s->fp = NULL;
|
||||
if (err)
|
||||
rc = CDK_File_Error;
|
||||
}
|
||||
|
||||
/* Iterate over the filter list and use the cleanup flag to
|
||||
free the allocated internal structures. */
|
||||
f = s->filters;
|
||||
while (f)
|
||||
{
|
||||
f2 = f->next;
|
||||
if (f->fnct)
|
||||
f->fnct (f->opaque, STREAMCTL_FREE, NULL, NULL);
|
||||
cdk_free (f);
|
||||
f = f2;
|
||||
}
|
||||
|
||||
if (s->fname)
|
||||
{
|
||||
cdk_free (s->fname);
|
||||
s->fname = NULL;
|
||||
}
|
||||
|
||||
cdk_free (s->cache.buf);
|
||||
s->cache.alloced = 0;
|
||||
|
||||
cdk_free (s);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_eof: Return if the associated file handle was set to EOF.
|
||||
* @s: The STREAM object.
|
||||
*
|
||||
* This function will only work with read streams.
|
||||
**/
|
||||
int
|
||||
cdk_stream_eof (cdk_stream_t s)
|
||||
{
|
||||
return s ? s->flags.eof : -1;
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
_cdk_stream_get_fname (cdk_stream_t s)
|
||||
{
|
||||
if (!s)
|
||||
return NULL;
|
||||
if (s->flags.temp)
|
||||
return NULL;
|
||||
return s->fname ? s->fname : NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Return the underlying FP of the stream.
|
||||
WARNING: This handle should not be closed. */
|
||||
FILE *
|
||||
_cdk_stream_get_fp (cdk_stream_t s)
|
||||
{
|
||||
return s ? s->fp : NULL;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
_cdk_stream_get_errno (cdk_stream_t s)
|
||||
{
|
||||
return s ? s->error : CDK_Inv_Value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_get_length: Return the length of the associated file handle.
|
||||
* @s: The STREAM object.
|
||||
*
|
||||
* This function should work for both read and write streams. For write
|
||||
* streams an additional flush is used to write possible pending data.
|
||||
**/
|
||||
off_t
|
||||
cdk_stream_get_length (cdk_stream_t s)
|
||||
{
|
||||
struct stat statbuf;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!s)
|
||||
return (off_t) - 1;
|
||||
|
||||
/* The user callback does not support stat. */
|
||||
if (s->cbs_hd)
|
||||
return 0;
|
||||
|
||||
rc = stream_flush (s);
|
||||
if (rc)
|
||||
{
|
||||
s->error = rc;
|
||||
return (off_t) - 1;
|
||||
}
|
||||
|
||||
if (fstat (fileno (s->fp), &statbuf))
|
||||
{
|
||||
s->error = CDK_File_Error;
|
||||
return (off_t) - 1;
|
||||
}
|
||||
|
||||
return statbuf.st_size;
|
||||
}
|
||||
|
||||
|
||||
static struct stream_filter_s *
|
||||
filter_add2 (cdk_stream_t s)
|
||||
{
|
||||
struct stream_filter_s *f;
|
||||
|
||||
assert (s);
|
||||
|
||||
f = cdk_calloc (1, sizeof *f);
|
||||
if (!f)
|
||||
return NULL;
|
||||
f->next = s->filters;
|
||||
s->filters = f;
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
static struct stream_filter_s *
|
||||
filter_search (cdk_stream_t s, filter_fnct_t fnc)
|
||||
{
|
||||
struct stream_filter_s *f;
|
||||
|
||||
assert (s);
|
||||
|
||||
for (f = s->filters; f; f = f->next)
|
||||
{
|
||||
if (f->fnct == fnc)
|
||||
return f;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
struct stream_filter_s *
|
||||
filter_add (cdk_stream_t s, filter_fnct_t fnc, int type)
|
||||
{
|
||||
struct stream_filter_s *f;
|
||||
|
||||
assert (s);
|
||||
|
||||
s->flags.filtrated = 0;
|
||||
f = filter_search (s, fnc);
|
||||
if (f)
|
||||
return f;
|
||||
f = filter_add2 (s);
|
||||
if (!f)
|
||||
return NULL;
|
||||
f->fnct = fnc;
|
||||
f->flags.enabled = 1;
|
||||
f->tmp = NULL;
|
||||
f->type = type;
|
||||
switch (type)
|
||||
{
|
||||
case fARMOR:
|
||||
f->opaque = &f->u.afx;
|
||||
break;
|
||||
case fCIPHER:
|
||||
f->opaque = &f->u.cfx;
|
||||
break;
|
||||
case fLITERAL:
|
||||
f->opaque = &f->u.pfx;
|
||||
break;
|
||||
case fCOMPRESS:
|
||||
f->opaque = &f->u.zfx;
|
||||
break;
|
||||
case fHASH:
|
||||
f->opaque = &f->u.mfx;
|
||||
break;
|
||||
case fTEXT:
|
||||
f->opaque = &f->u.tfx;
|
||||
break;
|
||||
default:
|
||||
f->opaque = NULL;
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
stream_get_mode (cdk_stream_t s)
|
||||
{
|
||||
assert (s);
|
||||
|
||||
if (s->flags.temp)
|
||||
return s->fmode;
|
||||
return s->flags.write;
|
||||
}
|
||||
|
||||
|
||||
static filter_fnct_t
|
||||
stream_id_to_filter (int type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case fARMOR:
|
||||
return _cdk_filter_armor;
|
||||
case fLITERAL:
|
||||
return _cdk_filter_literal;
|
||||
case fTEXT:
|
||||
return _cdk_filter_text;
|
||||
case fCIPHER:
|
||||
return _cdk_filter_cipher;
|
||||
case fCOMPRESS:
|
||||
return _cdk_filter_compress;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_filter_disable: Disable the filter with the type 'type'
|
||||
* @s: The STREAM object
|
||||
* @type: The numberic filter ID.
|
||||
*
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_stream_filter_disable (cdk_stream_t s, int type)
|
||||
{
|
||||
struct stream_filter_s *f;
|
||||
filter_fnct_t fnc;
|
||||
|
||||
if (!s)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
fnc = stream_id_to_filter (type);
|
||||
if (!fnc)
|
||||
return CDK_Inv_Value;
|
||||
f = filter_search (s, fnc);
|
||||
if (f)
|
||||
f->flags.enabled = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* WARNING: tmp should not be closed by the caller. */
|
||||
static cdk_error_t
|
||||
stream_fp_replace (cdk_stream_t s, FILE ** tmp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
assert (s);
|
||||
|
||||
_cdk_log_debug ("replace stream fd=%d with fd=%d\n",
|
||||
fileno (s->fp), fileno (*tmp));
|
||||
rc = fclose (s->fp);
|
||||
if (rc)
|
||||
return CDK_File_Error;
|
||||
s->fp = *tmp;
|
||||
*tmp = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* This function is exactly like filter_read, except the fact that we can't
|
||||
use tmpfile () all the time. That's why we open the real file when there
|
||||
is no last filter. */
|
||||
static cdk_error_t
|
||||
stream_filter_write (cdk_stream_t s)
|
||||
{
|
||||
struct stream_filter_s *f;
|
||||
cdk_error_t rc = 0;
|
||||
|
||||
assert (s);
|
||||
|
||||
if (s->flags.filtrated)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
for (f = s->filters; f; f = f->next)
|
||||
{
|
||||
if (!f->flags.enabled)
|
||||
continue;
|
||||
/* if there is no next filter, create the final output file */
|
||||
_cdk_log_debug ("filter [write]: last filter=%d fname=%s\n",
|
||||
f->next ? 1 : 0, s->fname);
|
||||
if (!f->next && s->fname)
|
||||
f->tmp = fopen (s->fname, "w+b");
|
||||
else
|
||||
f->tmp = my_tmpfile ();
|
||||
if (!f->tmp)
|
||||
{
|
||||
rc = CDK_File_Error;
|
||||
break;
|
||||
}
|
||||
/* If there is no next filter, flush the cache. We also do this
|
||||
when the next filter is the armor filter because this filter
|
||||
is special and before it starts, all data should be written. */
|
||||
if ((!f->next || f->next->type == fARMOR) && s->cache.size)
|
||||
{
|
||||
rc = stream_cache_flush (s, f->tmp);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
rc = f->fnct (f->opaque, f->ctl, s->fp, f->tmp);
|
||||
_cdk_log_debug ("filter [write]: type=%d rc=%d\n", f->type, rc);
|
||||
if (!rc)
|
||||
rc = stream_fp_replace (s, &f->tmp);
|
||||
if (!rc)
|
||||
rc = cdk_stream_seek (s, 0);
|
||||
if (rc)
|
||||
{
|
||||
_cdk_log_debug ("filter [close]: fd=%d\n", fileno (f->tmp));
|
||||
fclose (f->tmp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Here all data from the file handle is passed through all filters.
|
||||
The scheme works like this:
|
||||
Create a tempfile and use it for the output of the filter. Then the
|
||||
original file handle will be closed and replace with the temp handle.
|
||||
The file pointer will be set to the begin and the game starts again. */
|
||||
static cdk_error_t
|
||||
stream_filter_read (cdk_stream_t s)
|
||||
{
|
||||
struct stream_filter_s *f;
|
||||
cdk_error_t rc = 0;
|
||||
|
||||
assert (s);
|
||||
|
||||
if (s->flags.filtrated)
|
||||
return 0;
|
||||
|
||||
for (f = s->filters; f; f = f->next)
|
||||
{
|
||||
if (!f->flags.enabled)
|
||||
continue;
|
||||
if (f->flags.error)
|
||||
{
|
||||
_cdk_log_debug ("filter %s [read]: has the error flag; skipped\n",
|
||||
s->fname ? s->fname : "[temp]");
|
||||
continue;
|
||||
}
|
||||
|
||||
f->tmp = my_tmpfile ();
|
||||
if (!f->tmp)
|
||||
{
|
||||
rc = CDK_File_Error;
|
||||
break;
|
||||
}
|
||||
rc = f->fnct (f->opaque, f->ctl, s->fp, f->tmp);
|
||||
_cdk_log_debug ("filter %s [read]: type=%d rc=%d\n",
|
||||
s->fname ? s->fname : "[temp]", f->type, rc);
|
||||
if (rc)
|
||||
{
|
||||
f->flags.error = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
f->flags.error = 0;
|
||||
/* If the filter is read-only, do not replace the FP because
|
||||
the contents were not altered in any way. */
|
||||
if (!f->flags.rdonly)
|
||||
{
|
||||
rc = stream_fp_replace (s, &f->tmp);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
fclose (f->tmp);
|
||||
f->tmp = NULL;
|
||||
}
|
||||
rc = cdk_stream_seek (s, 0);
|
||||
if (rc)
|
||||
break;
|
||||
/* Disable the filter after it was successfully used. The idea
|
||||
is the following: let's say the armor filter was pushed and
|
||||
later more filters were added. The second time the filter code
|
||||
will be executed, only the new filter should be started but
|
||||
not the old because we already used it. */
|
||||
f->flags.enabled = 0;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
_cdk_stream_get_opaque (cdk_stream_t s, int fid)
|
||||
{
|
||||
struct stream_filter_s *f;
|
||||
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
for (f = s->filters; f; f = f->next)
|
||||
{
|
||||
if (f->type == fid)
|
||||
return f->opaque;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_read: Try to read count bytes from the STREAM object.
|
||||
* @s: The STREAM object.
|
||||
* @buf: The buffer to insert the readed bytes.
|
||||
* @count: Request so much bytes.
|
||||
*
|
||||
* When this function is called the first time, it can take a while
|
||||
* because all filters need to be processed. Please remember that you
|
||||
* need to add the filters in reserved order.
|
||||
**/
|
||||
int
|
||||
cdk_stream_read (cdk_stream_t s, void *buf, size_t buflen)
|
||||
{
|
||||
int nread;
|
||||
int rc;
|
||||
|
||||
if (!s)
|
||||
{
|
||||
s->error = CDK_Inv_Value;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
if (s->cbs_hd)
|
||||
{
|
||||
if (s->cbs.read)
|
||||
return s->cbs.read (s->cbs_hd, buf, buflen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (s->flags.write && !s->flags.temp)
|
||||
{
|
||||
s->error = CDK_Inv_Mode;
|
||||
return EOF; /* This is a write stream */
|
||||
}
|
||||
|
||||
if (!s->flags.no_filter && !s->cache.on && !s->flags.filtrated)
|
||||
{
|
||||
rc = stream_filter_read (s);
|
||||
if (rc)
|
||||
{
|
||||
s->error = rc;
|
||||
if (feof (s->fp))
|
||||
s->flags.eof = 1;
|
||||
return EOF;
|
||||
}
|
||||
s->flags.filtrated = 1;
|
||||
}
|
||||
if (!buf && !buflen)
|
||||
return 0;
|
||||
nread = fread (buf, 1, buflen, s->fp);
|
||||
if (!nread)
|
||||
nread = EOF;
|
||||
if (feof (s->fp))
|
||||
{
|
||||
s->error = 0;
|
||||
s->flags.eof = 1;
|
||||
}
|
||||
return nread;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
cdk_stream_getc (cdk_stream_t s)
|
||||
{
|
||||
unsigned char buf[2];
|
||||
int nread;
|
||||
|
||||
if (!s)
|
||||
{
|
||||
s->error = CDK_Inv_Value;
|
||||
return EOF;
|
||||
}
|
||||
nread = cdk_stream_read (s, buf, 1);
|
||||
if (nread == EOF)
|
||||
{
|
||||
s->error = CDK_File_Error;
|
||||
return EOF;
|
||||
}
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_write: Try to write count bytes into the stream.
|
||||
* @s: The STREAM object
|
||||
* @buf: The buffer with the values to write.
|
||||
* @count: The size of the buffer.
|
||||
*
|
||||
* In this function we simply write the bytes to the stream. We can't
|
||||
* use the filters here because it would mean they have to support
|
||||
* partial flushing.
|
||||
**/
|
||||
int
|
||||
cdk_stream_write (cdk_stream_t s, const void *buf, size_t count)
|
||||
{
|
||||
int nwritten;
|
||||
|
||||
if (!s)
|
||||
{
|
||||
s->error = CDK_Inv_Value;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
if (s->cbs_hd)
|
||||
{
|
||||
if (s->cbs.write)
|
||||
return s->cbs.write (s->cbs_hd, buf, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!s->flags.write)
|
||||
{
|
||||
s->error = CDK_Inv_Mode; /* this is a read stream */
|
||||
return EOF;
|
||||
}
|
||||
|
||||
if (!buf && !count)
|
||||
return stream_flush (s);
|
||||
|
||||
if (s->cache.on)
|
||||
{
|
||||
/* We need to resize the buffer if the additional data wouldn't
|
||||
fit into it. We allocate more memory to avoid to resize it the
|
||||
next time the function is used. */
|
||||
if (s->cache.size + count > s->cache.alloced)
|
||||
{
|
||||
byte *old = s->cache.buf;
|
||||
|
||||
s->cache.buf =
|
||||
cdk_calloc (1, s->cache.alloced + count + STREAM_BUFSIZE);
|
||||
s->cache.alloced += (count + STREAM_BUFSIZE);
|
||||
memcpy (s->cache.buf, old, s->cache.size);
|
||||
cdk_free (old);
|
||||
_cdk_log_debug ("stream: enlarge cache to %d octets\n",
|
||||
s->cache.alloced);
|
||||
}
|
||||
memcpy (s->cache.buf + s->cache.size, buf, count);
|
||||
s->cache.size += count;
|
||||
return count;
|
||||
}
|
||||
|
||||
nwritten = fwrite (buf, 1, count, s->fp);
|
||||
if (!nwritten)
|
||||
nwritten = EOF;
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
cdk_stream_putc (cdk_stream_t s, int c)
|
||||
{
|
||||
byte buf[2];
|
||||
int nwritten;
|
||||
|
||||
if (!s)
|
||||
{
|
||||
s->error = CDK_Inv_Value;
|
||||
return EOF;
|
||||
}
|
||||
buf[0] = c;
|
||||
nwritten = cdk_stream_write (s, buf, 1);
|
||||
if (nwritten == EOF)
|
||||
return EOF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
off_t
|
||||
cdk_stream_tell (cdk_stream_t s)
|
||||
{
|
||||
return s ? ftell (s->fp) : (off_t) - 1;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
cdk_stream_seek (cdk_stream_t s, off_t offset)
|
||||
{
|
||||
off_t len;
|
||||
|
||||
if (!s)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (s->cbs_hd)
|
||||
{
|
||||
if (s->cbs.seek)
|
||||
return s->cbs.seek (s->cbs_hd, offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set or reset the EOF flag. */
|
||||
len = cdk_stream_get_length (s);
|
||||
if (len == offset)
|
||||
s->flags.eof = 1;
|
||||
else
|
||||
s->flags.eof = 0;
|
||||
|
||||
if (fseek (s->fp, offset, SEEK_SET))
|
||||
return CDK_File_Error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
stream_flush (cdk_stream_t s)
|
||||
{
|
||||
assert (s);
|
||||
|
||||
/* For some constellations it cannot be assured that the
|
||||
return value is defined, thus we ignore it for now. */
|
||||
(void) fflush (s->fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_set_armor_flag:
|
||||
* @s: the stream object
|
||||
* @type: the type of armor to use
|
||||
*
|
||||
* If the file is in read-mode, no armor type needs to be
|
||||
* defined (armor_type=0) because the armor filter will be
|
||||
* used for decoding existing armor data.
|
||||
* For the write mode, @armor_type can be set to any valid
|
||||
* armor type (message, key, sig).
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_stream_set_armor_flag (cdk_stream_t s, int armor_type)
|
||||
{
|
||||
struct stream_filter_s *f;
|
||||
|
||||
if (!s)
|
||||
return CDK_Inv_Value;
|
||||
f = filter_add (s, _cdk_filter_armor, fARMOR);
|
||||
if (!f)
|
||||
return CDK_Out_Of_Core;
|
||||
f->u.afx.idx = f->u.afx.idx2 = armor_type;
|
||||
f->ctl = stream_get_mode (s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_set_literal_flag:
|
||||
* @s: the stream object
|
||||
* @mode: the mode to use (binary, text, unicode)
|
||||
* @fname: the file name to store in the packet.
|
||||
*
|
||||
* In read mode it kicks off the literal decoding routine to
|
||||
* unwrap the data from the packet. The @mode parameter is ignored.
|
||||
* In write mode the function can be used to wrap the stream data
|
||||
* into a literal packet with the given mode and file name.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_stream_set_literal_flag (cdk_stream_t s, cdk_lit_format_t mode,
|
||||
const char *fname)
|
||||
{
|
||||
struct stream_filter_s *f;
|
||||
const char *orig_fname;
|
||||
|
||||
_cdk_log_debug ("stream: enable literal mode.\n");
|
||||
|
||||
if (!s)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
orig_fname = _cdk_stream_get_fname (s);
|
||||
f = filter_add (s, _cdk_filter_literal, fLITERAL);
|
||||
if (!f)
|
||||
return CDK_Out_Of_Core;
|
||||
f->u.pfx.mode = mode;
|
||||
f->u.pfx.filename = fname ? cdk_strdup (fname) : NULL;
|
||||
f->u.pfx.orig_filename = orig_fname ? cdk_strdup (orig_fname) : NULL;
|
||||
f->ctl = stream_get_mode (s);
|
||||
if (s->blkmode > 0)
|
||||
{
|
||||
f->u.pfx.blkmode.on = 1;
|
||||
f->u.pfx.blkmode.size = s->blkmode;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_set_cipher_flag:
|
||||
* @s: the stream object
|
||||
* @dek: the data encryption key
|
||||
* @use_mdc: 1 means to use the MDC mode
|
||||
*
|
||||
* In read mode it kicks off the cipher filter to decrypt the data
|
||||
* from the stream with the key given in @dek.
|
||||
* In write mode the stream data will be encrypted with the DEK object
|
||||
* and optionally, the @use_mdc parameter can be used to enable the MDC mode.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_stream_set_cipher_flag (cdk_stream_t s, cdk_dek_t dek, int use_mdc)
|
||||
{
|
||||
struct stream_filter_s *f;
|
||||
|
||||
_cdk_log_debug ("stream: enable cipher mode\n");
|
||||
if (!s)
|
||||
return CDK_Inv_Value;
|
||||
f = filter_add (s, _cdk_filter_cipher, fCIPHER);
|
||||
if (!f)
|
||||
return CDK_Out_Of_Core;
|
||||
dek->use_mdc = use_mdc;
|
||||
f->ctl = stream_get_mode (s);
|
||||
f->u.cfx.dek = dek;
|
||||
f->u.cfx.mdc_method = use_mdc ? GCRY_MD_SHA1 : 0;
|
||||
if (s->blkmode > 0)
|
||||
{
|
||||
f->u.cfx.blkmode.on = 1;
|
||||
f->u.cfx.blkmode.size = s->blkmode;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_set_compress_flag:
|
||||
* @s: the stream object
|
||||
* @algo: the compression algo
|
||||
* @level: level of compression (0..9)
|
||||
*
|
||||
* In read mode it kicks off the decompression filter to retrieve
|
||||
* the uncompressed data.
|
||||
* In write mode the stream data will be compressed with the
|
||||
* given algorithm at the given level.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_stream_set_compress_flag (cdk_stream_t s, int algo, int level)
|
||||
{
|
||||
struct stream_filter_s *f;
|
||||
|
||||
if (!s)
|
||||
return CDK_Inv_Value;
|
||||
f = filter_add (s, _cdk_filter_compress, fCOMPRESS);
|
||||
if (!f)
|
||||
return CDK_Out_Of_Core;
|
||||
f->ctl = stream_get_mode (s);
|
||||
f->u.zfx.algo = algo;
|
||||
f->u.zfx.level = level;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_set_text_flag:
|
||||
* @s: the stream object
|
||||
* @lf: line ending
|
||||
*
|
||||
* Pushes the text filter to store the stream data in cannoncial format.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_stream_set_text_flag (cdk_stream_t s, const char *lf)
|
||||
{
|
||||
struct stream_filter_s *f;
|
||||
|
||||
if (!s)
|
||||
return CDK_Inv_Value;
|
||||
f = filter_add (s, _cdk_filter_text, fTEXT);
|
||||
if (!f)
|
||||
return CDK_Out_Of_Core;
|
||||
f->ctl = stream_get_mode (s);
|
||||
f->u.tfx.lf = lf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_set_hash_flag:
|
||||
* @s: the stream object
|
||||
* @digest_algo: the digest algorithm to use
|
||||
*
|
||||
* This is for read-only streams. It pushes a digest filter to
|
||||
* calculate the digest of the given stream data.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_stream_set_hash_flag (cdk_stream_t s, int digest_algo)
|
||||
{
|
||||
struct stream_filter_s *f;
|
||||
|
||||
if (!s)
|
||||
return CDK_Inv_Value;
|
||||
if (stream_get_mode (s))
|
||||
return CDK_Inv_Mode;
|
||||
f = filter_add (s, _cdk_filter_hash, fHASH);
|
||||
if (!f)
|
||||
return CDK_Out_Of_Core;
|
||||
f->ctl = stream_get_mode (s);
|
||||
f->u.mfx.digest_algo = digest_algo;
|
||||
f->flags.rdonly = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_enable_cache:
|
||||
* @s: the stream object
|
||||
* @val: 1=on, 0=off
|
||||
*
|
||||
* Enable or disable the cache section of a stream object.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_stream_enable_cache (cdk_stream_t s, int val)
|
||||
{
|
||||
if (!s)
|
||||
return CDK_Inv_Value;
|
||||
if (!s->flags.write)
|
||||
return CDK_Inv_Mode;
|
||||
s->cache.on = val;
|
||||
if (!s->cache.buf)
|
||||
{
|
||||
s->cache.buf = cdk_calloc (1, STREAM_BUFSIZE);
|
||||
s->cache.alloced = STREAM_BUFSIZE;
|
||||
_cdk_log_debug ("stream: allocate cache of %d octets\n",
|
||||
STREAM_BUFSIZE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
stream_cache_flush (cdk_stream_t s, FILE * fp)
|
||||
{
|
||||
int nwritten;
|
||||
|
||||
assert (s);
|
||||
|
||||
/* FIXME: We should find a way to use cdk_stream_write here. */
|
||||
if (s->cache.size > 0)
|
||||
{
|
||||
nwritten = fwrite (s->cache.buf, 1, s->cache.size, fp);
|
||||
if (!nwritten)
|
||||
return CDK_File_Error;
|
||||
s->cache.size = 0;
|
||||
s->cache.on = 0;
|
||||
wipemem (s->cache.buf, s->cache.alloced);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_kick_off:
|
||||
* @inp: the input stream
|
||||
* @out: the output stream.
|
||||
*
|
||||
* Passes the entire data from @inp into the output stream @out
|
||||
* with all the activated filters.
|
||||
*/
|
||||
cdk_error_t
|
||||
cdk_stream_kick_off (cdk_stream_t inp, cdk_stream_t out)
|
||||
{
|
||||
byte buf[BUFSIZE];
|
||||
int nread, nwritten;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!inp || !out)
|
||||
return CDK_Inv_Value;
|
||||
rc = CDK_Success;
|
||||
while (!cdk_stream_eof (inp))
|
||||
{
|
||||
nread = cdk_stream_read (inp, buf, DIM (buf));
|
||||
if (!nread || nread == EOF)
|
||||
break;
|
||||
nwritten = cdk_stream_write (out, buf, nread);
|
||||
if (!nwritten || nwritten == EOF)
|
||||
{ /* In case of errors, we leave the loop. */
|
||||
rc = inp->error;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
wipemem (buf, sizeof (buf));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_mmap_part:
|
||||
* @s: the stream
|
||||
* @off: the offset where to start
|
||||
* @len: how much bytes shall be mapped
|
||||
* @ret_buf: the buffer to store the content
|
||||
* @ret_buflen: length of the buffer
|
||||
*
|
||||
* Map the data of the given stream into a memory section. @ret_count
|
||||
* contains the length of the buffer.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_stream_mmap_part (cdk_stream_t s, off_t off, size_t len,
|
||||
byte ** ret_buf, size_t * ret_buflen)
|
||||
{
|
||||
off_t oldpos;
|
||||
int n;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!s || !ret_buf || !ret_buflen)
|
||||
return CDK_Inv_Value;
|
||||
if (s->cbs_hd)
|
||||
return CDK_Inv_Mode;
|
||||
|
||||
*ret_buflen = 0;
|
||||
*ret_buf = NULL;
|
||||
oldpos = cdk_stream_tell (s);
|
||||
rc = cdk_stream_flush (s);
|
||||
if (!rc)
|
||||
rc = cdk_stream_seek (s, off);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (!len)
|
||||
len = cdk_stream_get_length (s);
|
||||
if (!len || len > MAX_MAP_SIZE)
|
||||
return 0;
|
||||
*ret_buf = cdk_calloc (1, len + 1);
|
||||
*ret_buflen = len;
|
||||
n = cdk_stream_read (s, *ret_buf, len);
|
||||
if (n != len)
|
||||
*ret_buflen = n;
|
||||
rc = cdk_stream_seek (s, oldpos);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
cdk_stream_mmap (cdk_stream_t inp, byte ** buf, size_t * buflen)
|
||||
{
|
||||
off_t len;
|
||||
|
||||
/* We need to make sure all data is flushed before we retrieve the size. */
|
||||
cdk_stream_flush (inp);
|
||||
len = cdk_stream_get_length (inp);
|
||||
return cdk_stream_mmap_part (inp, 0, len, buf, buflen);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_peek:
|
||||
* @inp: the input stream handle
|
||||
* @s: buffer
|
||||
* @count: number of bytes to peek
|
||||
*
|
||||
* The function acts like cdk_stream_read with the difference that
|
||||
* the file pointer is moved to the old position after the bytes were read.
|
||||
**/
|
||||
int
|
||||
cdk_stream_peek (cdk_stream_t inp, byte * buf, size_t buflen)
|
||||
{
|
||||
off_t off;
|
||||
int nbytes;
|
||||
|
||||
if (!inp || !buf)
|
||||
return 0;
|
||||
if (inp->cbs_hd)
|
||||
return 0;
|
||||
|
||||
off = cdk_stream_tell (inp);
|
||||
nbytes = cdk_stream_read (inp, buf, buflen);
|
||||
if (nbytes == -1)
|
||||
return 0;
|
||||
if (cdk_stream_seek (inp, off))
|
||||
return 0;
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
|
||||
/* Try to read a line from the given stream. */
|
||||
int
|
||||
_cdk_stream_gets (cdk_stream_t s, char *buf, size_t count)
|
||||
{
|
||||
int c, i;
|
||||
|
||||
assert (s);
|
||||
|
||||
i = 0;
|
||||
while (!cdk_stream_eof (s) && count > 0)
|
||||
{
|
||||
c = cdk_stream_getc (s);
|
||||
if (c == EOF || c == '\r' || c == '\n')
|
||||
{
|
||||
buf[i++] = '\0';
|
||||
break;
|
||||
}
|
||||
buf[i++] = c;
|
||||
count--;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
/* Try to write string into the stream @s. */
|
||||
int
|
||||
_cdk_stream_puts (cdk_stream_t s, const char *buf)
|
||||
{
|
||||
return cdk_stream_write (s, buf, strlen (buf));
|
||||
}
|
||||
|
||||
|
||||
/* Activate the block mode for the given stream. */
|
||||
cdk_error_t
|
||||
_cdk_stream_set_blockmode (cdk_stream_t s, size_t nbytes)
|
||||
{
|
||||
assert (s);
|
||||
|
||||
_cdk_log_debug ("stream: activate block mode with blocksize %d\n", nbytes);
|
||||
s->blkmode = nbytes;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Return the block mode state of the given stream. */
|
||||
int
|
||||
_cdk_stream_get_blockmode (cdk_stream_t s)
|
||||
{
|
||||
return s ? s->blkmode : 0;
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/* stream.h - internal definiton for the STREAM object
|
||||
* Copyright (C) 2002, 2003, 2007 Timo Schulz
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifndef CDK_STREAM_H
|
||||
#define CDK_STREAM_H
|
||||
|
||||
/* The default buffer size for the stream. */
|
||||
#define STREAM_BUFSIZE 8192
|
||||
|
||||
enum {
|
||||
fDUMMY = 0,
|
||||
fARMOR = 1,
|
||||
fCIPHER = 2,
|
||||
fLITERAL = 3,
|
||||
fCOMPRESS= 4,
|
||||
fHASH = 5,
|
||||
fTEXT = 6
|
||||
};
|
||||
|
||||
/* Type definition for the filter function. */
|
||||
typedef int (*filter_fnct_t) (void * opaque, int ctl, FILE * in, FILE * out);
|
||||
|
||||
/* The stream filter context structure. */
|
||||
struct stream_filter_s
|
||||
{
|
||||
struct stream_filter_s *next;
|
||||
filter_fnct_t fnct;
|
||||
void *opaque;
|
||||
FILE *tmp;
|
||||
union {
|
||||
armor_filter_t afx;
|
||||
cipher_filter_t cfx;
|
||||
literal_filter_t pfx;
|
||||
compress_filter_t zfx;
|
||||
text_filter_t tfx;
|
||||
md_filter_t mfx;
|
||||
} u;
|
||||
struct {
|
||||
unsigned enabled:1;
|
||||
unsigned rdonly:1;
|
||||
unsigned error:1;
|
||||
} flags;
|
||||
unsigned type;
|
||||
unsigned ctl;
|
||||
};
|
||||
|
||||
|
||||
/* The stream context structure. */
|
||||
struct cdk_stream_s {
|
||||
struct stream_filter_s *filters;
|
||||
int fmode;
|
||||
int error;
|
||||
size_t blkmode;
|
||||
struct {
|
||||
unsigned filtrated:1;
|
||||
unsigned eof:1;
|
||||
unsigned write:1;
|
||||
unsigned temp:1;
|
||||
unsigned reset:1;
|
||||
unsigned no_filter:1;
|
||||
unsigned compressed:3;
|
||||
} flags;
|
||||
struct {
|
||||
unsigned char *buf;
|
||||
unsigned on:1;
|
||||
size_t size;
|
||||
size_t alloced;
|
||||
} cache;
|
||||
char *fname;
|
||||
FILE *fp;
|
||||
unsigned int fp_ref:1;
|
||||
struct cdk_stream_cbs_s cbs;
|
||||
void *cbs_hd;
|
||||
};
|
||||
|
||||
#endif /* CDK_STREAM_H */
|
||||
@@ -1,44 +0,0 @@
|
||||
/* types.h - Some type definitions
|
||||
* Copyright (C) 2002, 2003, 2007 Timo Schulz
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifndef CDK_TYPES_H
|
||||
#define CDK_TYPES_H
|
||||
|
||||
#include <gcrypt.h>
|
||||
|
||||
#ifndef HAVE_BYTE_TYPEDEF
|
||||
# undef byte
|
||||
typedef unsigned char byte;
|
||||
# define HAVE_BYTE_TYPEDEF
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_U16_TYPEDEF
|
||||
# undef u16
|
||||
typedef unsigned short u16;
|
||||
# define HAVE_U16_TYPEDEF
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_U32_TYPEDEF
|
||||
# undef u32
|
||||
typedef unsigned int u32;
|
||||
# define HAVE_U32_TYPEDEF
|
||||
#endif
|
||||
|
||||
#ifndef DIM
|
||||
# define DIM(v) (sizeof (v)/sizeof ((v)[0]))
|
||||
# define DIMof(type, member) DIM(((type *)0)->member)
|
||||
#endif
|
||||
|
||||
#endif /* CDK_TYPES_H */
|
||||
@@ -1,306 +0,0 @@
|
||||
/* verify.c - Verify signatures
|
||||
* Copyright (C) 2001, 2002, 2003, 2007 Timo Schulz
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "opencdk.h"
|
||||
#include "main.h"
|
||||
#include "filters.h"
|
||||
#include "packet.h"
|
||||
|
||||
|
||||
/* Table of all supported digest algorithms and their names. */
|
||||
struct
|
||||
{
|
||||
const char *name;
|
||||
int algo;
|
||||
} digest_table[] =
|
||||
{
|
||||
{
|
||||
"MD5", GCRY_MD_MD5},
|
||||
{
|
||||
"SHA1", GCRY_MD_SHA1},
|
||||
{
|
||||
"RIPEMD160", GCRY_MD_RMD160},
|
||||
{
|
||||
"SHA256", GCRY_MD_SHA256},
|
||||
{
|
||||
"SHA384", GCRY_MD_SHA384},
|
||||
{
|
||||
"SHA512", GCRY_MD_SHA512},
|
||||
{
|
||||
NULL, 0}
|
||||
};
|
||||
|
||||
|
||||
static int file_verify_clearsign (cdk_ctx_t, const char *, const char *);
|
||||
|
||||
|
||||
/**
|
||||
* cdk_stream_verify:
|
||||
* @hd: session handle
|
||||
* @inp: the input stream
|
||||
* @data: for detached signatures, this is the data stream @inp is the sig
|
||||
* @out: where the output shall be written.
|
||||
*/
|
||||
cdk_error_t
|
||||
cdk_stream_verify (cdk_ctx_t hd, cdk_stream_t inp, cdk_stream_t data,
|
||||
cdk_stream_t out)
|
||||
{
|
||||
/* FIXME: out is not currently used. */
|
||||
if (cdk_armor_filter_use (inp))
|
||||
cdk_stream_set_armor_flag (inp, 0);
|
||||
return _cdk_proc_packets (hd, inp, data, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_file_verify:
|
||||
* @hd: the session handle
|
||||
* @file: the input file
|
||||
* @data_file: for detached signature this is the data file and @file is the sig.
|
||||
* @output: the output file
|
||||
*
|
||||
* Verify a signature.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_file_verify (cdk_ctx_t hd, const char *file, const char *data_file,
|
||||
const char *output)
|
||||
{
|
||||
struct stat stbuf;
|
||||
cdk_stream_t inp, data;
|
||||
char buf[4096];
|
||||
int n;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!hd || !file)
|
||||
return CDK_Inv_Value;
|
||||
if (output && !hd->opt.overwrite && !stat (output, &stbuf))
|
||||
return CDK_Inv_Mode;
|
||||
|
||||
rc = cdk_stream_open (file, &inp);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (cdk_armor_filter_use (inp))
|
||||
{
|
||||
n = cdk_stream_peek (inp, (byte *) buf, DIM (buf) - 1);
|
||||
if (!n || n == -1)
|
||||
return CDK_EOF;
|
||||
buf[n] = '\0';
|
||||
if (strstr (buf, "BEGIN PGP SIGNED MESSAGE"))
|
||||
{
|
||||
cdk_stream_close (inp);
|
||||
return file_verify_clearsign (hd, file, output);
|
||||
}
|
||||
cdk_stream_set_armor_flag (inp, 0);
|
||||
}
|
||||
|
||||
if (data_file)
|
||||
{
|
||||
rc = cdk_stream_open (data_file, &data);
|
||||
if (rc)
|
||||
{
|
||||
cdk_stream_close (inp);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
else
|
||||
data = NULL;
|
||||
|
||||
rc = _cdk_proc_packets (hd, inp, data, NULL, NULL, NULL);
|
||||
|
||||
if (data != NULL)
|
||||
cdk_stream_close (data);
|
||||
cdk_stream_close (inp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_cdk_result_verify_free (cdk_verify_result_t res)
|
||||
{
|
||||
if (!res)
|
||||
return;
|
||||
cdk_free (res->policy_url);
|
||||
cdk_free (res->sig_data);
|
||||
cdk_free (res);
|
||||
}
|
||||
|
||||
|
||||
cdk_verify_result_t
|
||||
_cdk_result_verify_new (void)
|
||||
{
|
||||
cdk_verify_result_t res;
|
||||
|
||||
res = cdk_calloc (1, sizeof *res);
|
||||
if (!res)
|
||||
return NULL;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
file_verify_clearsign (cdk_ctx_t hd, const char *file, const char *output)
|
||||
{
|
||||
cdk_stream_t inp = NULL, out = NULL, tmp = NULL;
|
||||
gcry_md_hd_t md = NULL;
|
||||
char buf[512], chk[512];
|
||||
const char *s;
|
||||
int i, is_signed = 0, nbytes;
|
||||
int digest_algo = 0;
|
||||
gcry_error_t err;
|
||||
cdk_error_t rc;
|
||||
|
||||
if (output)
|
||||
{
|
||||
rc = cdk_stream_create (output, &out);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = cdk_stream_open (file, &inp);
|
||||
if (rc)
|
||||
{
|
||||
if (output)
|
||||
cdk_stream_close (out);
|
||||
return rc;
|
||||
}
|
||||
|
||||
s = "-----BEGIN PGP SIGNED MESSAGE-----";
|
||||
while (!cdk_stream_eof (inp))
|
||||
{
|
||||
nbytes = _cdk_stream_gets (inp, buf, DIM (buf) - 1);
|
||||
if (!nbytes || nbytes == -1)
|
||||
break;
|
||||
if (!strncmp (buf, s, strlen (s)))
|
||||
{
|
||||
is_signed = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cdk_stream_eof (inp) && !is_signed)
|
||||
{
|
||||
rc = CDK_Armor_Error;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
while (!cdk_stream_eof (inp))
|
||||
{
|
||||
nbytes = _cdk_stream_gets (inp, buf, DIM (buf) - 1);
|
||||
if (!nbytes || nbytes == -1)
|
||||
break;
|
||||
if (nbytes == 1) /* Empty line */
|
||||
break;
|
||||
else if (!strncmp (buf, "Hash: ", 6))
|
||||
{
|
||||
for (i = 0; digest_table[i].name; i++)
|
||||
{
|
||||
if (!strcmp (buf + 6, digest_table[i].name))
|
||||
{
|
||||
digest_algo = digest_table[i].algo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (digest_algo && gcry_md_test_algo (digest_algo))
|
||||
{
|
||||
rc = CDK_Inv_Algo;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (!digest_algo)
|
||||
digest_algo = GCRY_MD_MD5;
|
||||
|
||||
err = gcry_md_open (&md, digest_algo, 0);
|
||||
if (err)
|
||||
{
|
||||
rc = map_gcry_error (err);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
s = "-----BEGIN PGP SIGNATURE-----";
|
||||
while (!cdk_stream_eof (inp))
|
||||
{
|
||||
nbytes = _cdk_stream_gets (inp, buf, DIM (buf) - 1);
|
||||
if (!nbytes || nbytes == -1)
|
||||
break;
|
||||
if (!strncmp (buf, s, strlen (s)))
|
||||
break;
|
||||
else
|
||||
{
|
||||
cdk_stream_peek (inp, (byte *) chk, DIM (chk) - 1);
|
||||
i = strncmp (chk, s, strlen (s));
|
||||
if (strlen (buf) == 0 && i == 0)
|
||||
continue; /* skip last '\n' */
|
||||
_cdk_trim_string (buf, i == 0 ? 0 : 1);
|
||||
gcry_md_write (md, buf, strlen (buf));
|
||||
}
|
||||
if (!strncmp (buf, "- ", 2)) /* FIXME: handle it recursive. */
|
||||
memmove (buf, buf + 2, nbytes - 2);
|
||||
if (out)
|
||||
{
|
||||
if (strstr (buf, "\r\n"))
|
||||
buf[strlen (buf) - 2] = '\0';
|
||||
cdk_stream_write (out, buf, strlen (buf));
|
||||
_cdk_stream_puts (out, _cdk_armor_get_lineend ());
|
||||
}
|
||||
}
|
||||
|
||||
/* We create a temporary stream object to store the
|
||||
signature data in there. */
|
||||
rc = cdk_stream_tmp_new (&tmp);
|
||||
if (rc)
|
||||
goto leave;
|
||||
|
||||
s = "-----BEGIN PGP SIGNATURE-----\n";
|
||||
_cdk_stream_puts (tmp, s);
|
||||
while (!cdk_stream_eof (inp))
|
||||
{
|
||||
nbytes = _cdk_stream_gets (inp, buf, DIM (buf) - 1);
|
||||
if (!nbytes || nbytes == -1)
|
||||
break;
|
||||
if (nbytes < (DIM (buf) - 3))
|
||||
{
|
||||
buf[nbytes - 1] = '\n';
|
||||
buf[nbytes] = '\0';
|
||||
}
|
||||
cdk_stream_write (tmp, buf, nbytes);
|
||||
}
|
||||
|
||||
/* FIXME: This code is not very elegant. */
|
||||
cdk_stream_tmp_set_mode (tmp, STREAMCTL_READ);
|
||||
cdk_stream_seek (tmp, 0);
|
||||
cdk_stream_set_armor_flag (tmp, 0);
|
||||
cdk_stream_read (tmp, NULL, 0);
|
||||
|
||||
/* the digest handle will be closed there. */
|
||||
rc = _cdk_proc_packets (hd, tmp, NULL, NULL, NULL, md);
|
||||
|
||||
leave:
|
||||
gcry_md_close (md);
|
||||
cdk_stream_close (out);
|
||||
cdk_stream_close (tmp);
|
||||
cdk_stream_close (inp);
|
||||
return rc;
|
||||
}
|
||||
@@ -1,947 +0,0 @@
|
||||
/* write-packet.c - Write OpenPGP packets
|
||||
* Copyright (C) 2001, 2002, 2003, 2007 Timo Schulz
|
||||
*
|
||||
* This file is part of OpenCDK.
|
||||
*
|
||||
* OpenCDK 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* OpenCDK 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.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "opencdk.h"
|
||||
#include "main.h"
|
||||
|
||||
|
||||
static int
|
||||
stream_write (cdk_stream_t s, const void *buf, size_t buflen)
|
||||
{
|
||||
int nwritten;
|
||||
|
||||
nwritten = cdk_stream_write (s, buf, buflen);
|
||||
if (nwritten == EOF)
|
||||
return _cdk_stream_get_errno (s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
stream_read (cdk_stream_t s, void *buf, size_t buflen, size_t * r_nread)
|
||||
{
|
||||
int nread;
|
||||
|
||||
assert (r_nread);
|
||||
|
||||
nread = cdk_stream_read (s, buf, buflen);
|
||||
if (nread == EOF)
|
||||
return _cdk_stream_get_errno (s);
|
||||
*r_nread = nread;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
stream_putc (cdk_stream_t s, int c)
|
||||
{
|
||||
int nwritten = cdk_stream_putc (s, c);
|
||||
if (nwritten == EOF)
|
||||
return _cdk_stream_get_errno (s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
write_32 (cdk_stream_t out, u32 u)
|
||||
{
|
||||
byte buf[4];
|
||||
|
||||
buf[0] = u >> 24;
|
||||
buf[1] = u >> 16;
|
||||
buf[2] = u >> 8;
|
||||
buf[3] = u;
|
||||
return stream_write (out, buf, 4);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
write_16 (cdk_stream_t out, u16 u)
|
||||
{
|
||||
byte buf[2];
|
||||
|
||||
buf[0] = u >> 8;
|
||||
buf[1] = u;
|
||||
return stream_write (out, buf, 2);
|
||||
}
|
||||
|
||||
|
||||
static size_t
|
||||
calc_mpisize (gcry_mpi_t mpi[MAX_CDK_PK_PARTS], size_t ncount)
|
||||
{
|
||||
size_t size, i;
|
||||
|
||||
size = 0;
|
||||
for (i = 0; i < ncount; i++)
|
||||
size += (gcry_mpi_get_nbits (mpi[i]) + 7) / 8 + 2;
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
write_mpi (cdk_stream_t out, gcry_mpi_t m)
|
||||
{
|
||||
byte buf[MAX_MPI_BYTES + 2];
|
||||
size_t nbits, nread;
|
||||
gcry_error_t err;
|
||||
|
||||
if (!out || !m)
|
||||
return CDK_Inv_Value;
|
||||
nbits = gcry_mpi_get_nbits (m);
|
||||
if (nbits > MAX_MPI_BITS || nbits < 1)
|
||||
return CDK_MPI_Error;
|
||||
err = gcry_mpi_print (GCRYMPI_FMT_PGP, buf, MAX_MPI_BYTES + 2, &nread, m);
|
||||
if (err)
|
||||
return map_gcry_error (err);
|
||||
return stream_write (out, buf, nread);
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
write_mpibuf (cdk_stream_t out, gcry_mpi_t mpi[MAX_CDK_PK_PARTS],
|
||||
size_t count)
|
||||
{
|
||||
size_t i;
|
||||
cdk_error_t rc;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
rc = write_mpi (out, mpi[i]);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
pkt_encode_len (cdk_stream_t out, size_t pktlen)
|
||||
{
|
||||
cdk_error_t rc;
|
||||
|
||||
assert (out);
|
||||
|
||||
rc = 0;
|
||||
if (!pktlen)
|
||||
{
|
||||
/* Block mode, partial bodies, with 'DEF_BLOCKSIZE' from main.h */
|
||||
rc = stream_putc (out, (0xE0 | DEF_BLOCKBITS));
|
||||
}
|
||||
else if (pktlen < 192)
|
||||
rc = stream_putc (out, pktlen);
|
||||
else if (pktlen < 8384)
|
||||
{
|
||||
pktlen -= 192;
|
||||
rc = stream_putc (out, (pktlen / 256) + 192);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, (pktlen % 256));
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = stream_putc (out, 255);
|
||||
if (!rc)
|
||||
rc = write_32 (out, pktlen);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
write_head_new (cdk_stream_t out, size_t size, int type)
|
||||
{
|
||||
cdk_error_t rc;
|
||||
|
||||
assert (out);
|
||||
|
||||
if (type < 0 || type > 63)
|
||||
return CDK_Inv_Packet;
|
||||
rc = stream_putc (out, (0xC0 | type));
|
||||
if (!rc)
|
||||
rc = pkt_encode_len (out, size);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
write_head_old (cdk_stream_t out, size_t size, int type)
|
||||
{
|
||||
cdk_error_t rc;
|
||||
int ctb;
|
||||
|
||||
assert (out);
|
||||
|
||||
if (type < 0 || type > 16)
|
||||
return CDK_Inv_Packet;
|
||||
ctb = 0x80 | (type << 2);
|
||||
if (!size)
|
||||
ctb |= 3;
|
||||
else if (size < 256)
|
||||
;
|
||||
else if (size < 65536)
|
||||
ctb |= 1;
|
||||
else
|
||||
ctb |= 2;
|
||||
rc = stream_putc (out, ctb);
|
||||
if (!size)
|
||||
return rc;
|
||||
if (!rc)
|
||||
{
|
||||
if (size < 256)
|
||||
rc = stream_putc (out, size);
|
||||
else if (size < 65536)
|
||||
rc = write_16 (out, size);
|
||||
else
|
||||
rc = write_32 (out, size);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Write special PGP2 packet header. PGP2 (wrongly) uses two byte header
|
||||
length for signatures and keys even if the size is < 256. */
|
||||
static cdk_error_t
|
||||
pkt_write_head2 (cdk_stream_t out, size_t size, int type)
|
||||
{
|
||||
cdk_error_t rc;
|
||||
|
||||
rc = cdk_stream_putc (out, 0x80 | (type << 2) | 1);
|
||||
if (!rc)
|
||||
rc = cdk_stream_putc (out, size >> 8);
|
||||
if (!rc)
|
||||
rc = cdk_stream_putc (out, size & 0xff);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
pkt_write_head (cdk_stream_t out, int old_ctb, size_t size, int type)
|
||||
{
|
||||
if (old_ctb)
|
||||
return write_head_old (out, size, type);
|
||||
return write_head_new (out, size, type);
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
write_encrypted (cdk_stream_t out, cdk_pkt_encrypted_t enc, int old_ctb)
|
||||
{
|
||||
size_t nbytes;
|
||||
cdk_error_t rc;
|
||||
|
||||
assert (out);
|
||||
assert (enc);
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("write_encrypted: %lu bytes\n", enc->len);
|
||||
|
||||
nbytes = enc->len ? (enc->len + enc->extralen) : 0;
|
||||
rc = pkt_write_head (out, old_ctb, nbytes, CDK_PKT_ENCRYPTED);
|
||||
/* The rest of the packet is ciphertext */
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
write_encrypted_mdc (cdk_stream_t out, cdk_pkt_encrypted_t enc)
|
||||
{
|
||||
size_t nbytes;
|
||||
cdk_error_t rc;
|
||||
|
||||
assert (out);
|
||||
assert (enc);
|
||||
|
||||
if (!enc->mdc_method)
|
||||
return CDK_Inv_Packet;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("write_encrypted_mdc: %lu bytes\n", enc->len);
|
||||
|
||||
nbytes = enc->len ? (enc->len + enc->extralen + 1) : 0;
|
||||
rc = pkt_write_head (out, 0, nbytes, CDK_PKT_ENCRYPTED_MDC);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, 1); /* version */
|
||||
/* The rest of the packet is ciphertext */
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
write_symkey_enc (cdk_stream_t out, cdk_pkt_symkey_enc_t ske)
|
||||
{
|
||||
cdk_s2k_t s2k;
|
||||
size_t size = 0, s2k_size = 0;
|
||||
cdk_error_t rc;
|
||||
|
||||
assert (out);
|
||||
assert (ske);
|
||||
|
||||
if (ske->version != 4)
|
||||
return CDK_Inv_Packet;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("write_symkey_enc:\n");
|
||||
|
||||
s2k = ske->s2k;
|
||||
if (s2k->mode == CDK_S2K_SALTED || s2k->mode == CDK_S2K_ITERSALTED)
|
||||
s2k_size = 8;
|
||||
if (s2k->mode == CDK_S2K_ITERSALTED)
|
||||
s2k_size++;
|
||||
size = 4 + s2k_size + ske->seskeylen;
|
||||
rc = pkt_write_head (out, 0, size, CDK_PKT_SYMKEY_ENC);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, ske->version);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, ske->cipher_algo);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, s2k->mode);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, s2k->hash_algo);
|
||||
if (s2k->mode == CDK_S2K_SALTED || s2k->mode == CDK_S2K_ITERSALTED)
|
||||
{
|
||||
rc = stream_write (out, s2k->salt, 8);
|
||||
if (!rc)
|
||||
{
|
||||
if (s2k->mode == CDK_S2K_ITERSALTED)
|
||||
rc = stream_putc (out, s2k->count);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
write_pubkey_enc (cdk_stream_t out, cdk_pkt_pubkey_enc_t pke, int old_ctb)
|
||||
{
|
||||
size_t size;
|
||||
int rc, nenc;
|
||||
|
||||
assert (out);
|
||||
assert (pke);
|
||||
|
||||
if (pke->version < 2 || pke->version > 3)
|
||||
return CDK_Inv_Packet;
|
||||
if (!KEY_CAN_ENCRYPT (pke->pubkey_algo))
|
||||
return CDK_Inv_Algo;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("write_pubkey_enc:\n");
|
||||
|
||||
nenc = cdk_pk_get_nenc (pke->pubkey_algo);
|
||||
size = 10 + calc_mpisize (pke->mpi, nenc);
|
||||
rc = pkt_write_head (out, old_ctb, size, CDK_PKT_PUBKEY_ENC);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = stream_putc (out, pke->version);
|
||||
if (!rc)
|
||||
rc = write_32 (out, pke->keyid[0]);
|
||||
if (!rc)
|
||||
rc = write_32 (out, pke->keyid[1]);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, pke->pubkey_algo);
|
||||
if (!rc)
|
||||
rc = write_mpibuf (out, pke->mpi, nenc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
write_mdc (cdk_stream_t out, cdk_pkt_mdc_t mdc)
|
||||
{
|
||||
cdk_error_t rc;
|
||||
|
||||
assert (mdc);
|
||||
assert (out);
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("write_mdc:\n");
|
||||
|
||||
/* This packet requires a fixed header encoding */
|
||||
rc = stream_putc (out, 0xD3); /* packet ID and 1 byte length */
|
||||
if (!rc)
|
||||
rc = stream_putc (out, 0x14);
|
||||
if (!rc)
|
||||
rc = stream_write (out, mdc->hash, DIM (mdc->hash));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static size_t
|
||||
calc_subpktsize (cdk_subpkt_t s)
|
||||
{
|
||||
size_t nbytes;
|
||||
|
||||
/* In the count mode, no buffer is returned. */
|
||||
_cdk_subpkt_get_array (s, 1, &nbytes);
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
write_v3_sig (cdk_stream_t out, cdk_pkt_signature_t sig, int nsig)
|
||||
{
|
||||
size_t size;
|
||||
cdk_error_t rc;
|
||||
|
||||
size = 19 + calc_mpisize (sig->mpi, nsig);
|
||||
if (is_RSA (sig->pubkey_algo))
|
||||
rc = pkt_write_head2 (out, size, CDK_PKT_SIGNATURE);
|
||||
else
|
||||
rc = pkt_write_head (out, 1, size, CDK_PKT_SIGNATURE);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, sig->version);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, 5);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, sig->sig_class);
|
||||
if (!rc)
|
||||
rc = write_32 (out, sig->timestamp);
|
||||
if (!rc)
|
||||
rc = write_32 (out, sig->keyid[0]);
|
||||
if (!rc)
|
||||
rc = write_32 (out, sig->keyid[1]);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, sig->pubkey_algo);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, sig->digest_algo);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, sig->digest_start[0]);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, sig->digest_start[1]);
|
||||
if (!rc)
|
||||
rc = write_mpibuf (out, sig->mpi, nsig);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
write_signature (cdk_stream_t out, cdk_pkt_signature_t sig, int old_ctb)
|
||||
{
|
||||
byte *buf;
|
||||
size_t nbytes, size, nsig;
|
||||
cdk_error_t rc;
|
||||
|
||||
assert (out);
|
||||
assert (sig);
|
||||
|
||||
if (!KEY_CAN_SIGN (sig->pubkey_algo))
|
||||
return CDK_Inv_Algo;
|
||||
if (sig->version < 2 || sig->version > 4)
|
||||
return CDK_Inv_Packet;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("write_signature:\n");
|
||||
|
||||
nsig = cdk_pk_get_nsig (sig->pubkey_algo);
|
||||
if (!nsig)
|
||||
return CDK_Inv_Algo;
|
||||
if (sig->version < 4)
|
||||
return write_v3_sig (out, sig, nsig);
|
||||
|
||||
size = 10 + calc_subpktsize (sig->hashed)
|
||||
+ calc_subpktsize (sig->unhashed) + calc_mpisize (sig->mpi, nsig);
|
||||
rc = pkt_write_head (out, 0, size, CDK_PKT_SIGNATURE);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, 4);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, sig->sig_class);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, sig->pubkey_algo);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, sig->digest_algo);
|
||||
if (!rc)
|
||||
rc = write_16 (out, sig->hashed_size);
|
||||
if (!rc)
|
||||
{
|
||||
buf = _cdk_subpkt_get_array (sig->hashed, 0, &nbytes);
|
||||
if (!buf)
|
||||
return CDK_Out_Of_Core;
|
||||
rc = stream_write (out, buf, nbytes);
|
||||
cdk_free (buf);
|
||||
}
|
||||
if (!rc)
|
||||
rc = write_16 (out, sig->unhashed_size);
|
||||
if (!rc)
|
||||
{
|
||||
buf = _cdk_subpkt_get_array (sig->unhashed, 0, &nbytes);
|
||||
if (!buf)
|
||||
return CDK_Out_Of_Core;
|
||||
rc = stream_write (out, buf, nbytes);
|
||||
cdk_free (buf);
|
||||
}
|
||||
if (!rc)
|
||||
rc = stream_putc (out, sig->digest_start[0]);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, sig->digest_start[1]);
|
||||
if (!rc)
|
||||
rc = write_mpibuf (out, sig->mpi, nsig);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
write_public_key (cdk_stream_t out, cdk_pkt_pubkey_t pk,
|
||||
int is_subkey, int old_ctb)
|
||||
{
|
||||
int pkttype, ndays = 0;
|
||||
size_t npkey = 0, size = 6;
|
||||
cdk_error_t rc;
|
||||
|
||||
assert (out);
|
||||
assert (pk);
|
||||
|
||||
if (pk->version < 2 || pk->version > 4)
|
||||
return CDK_Inv_Packet;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("write_public_key: subkey=%d\n", is_subkey);
|
||||
|
||||
pkttype = is_subkey ? CDK_PKT_PUBLIC_SUBKEY : CDK_PKT_PUBLIC_KEY;
|
||||
npkey = cdk_pk_get_npkey (pk->pubkey_algo);
|
||||
if (!npkey)
|
||||
return CDK_Inv_Algo;
|
||||
if (pk->version < 4)
|
||||
size += 2; /* expire date */
|
||||
if (is_subkey)
|
||||
old_ctb = 0;
|
||||
size += calc_mpisize (pk->mpi, npkey);
|
||||
if (old_ctb)
|
||||
rc = pkt_write_head2 (out, size, pkttype);
|
||||
else
|
||||
rc = pkt_write_head (out, old_ctb, size, pkttype);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, pk->version);
|
||||
if (!rc)
|
||||
rc = write_32 (out, pk->timestamp);
|
||||
if (!rc && pk->version < 4)
|
||||
{
|
||||
if (pk->expiredate)
|
||||
ndays = (u16) ((pk->expiredate - pk->timestamp) / 86400L);
|
||||
rc = write_16 (out, ndays);
|
||||
}
|
||||
if (!rc)
|
||||
rc = stream_putc (out, pk->pubkey_algo);
|
||||
if (!rc)
|
||||
rc = write_mpibuf (out, pk->mpi, npkey);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
calc_s2ksize (cdk_pkt_seckey_t sk)
|
||||
{
|
||||
size_t nbytes = 0;
|
||||
|
||||
if (!sk->is_protected)
|
||||
return 0;
|
||||
switch (sk->protect.s2k->mode)
|
||||
{
|
||||
case CDK_S2K_SIMPLE:
|
||||
nbytes = 2;
|
||||
break;
|
||||
case CDK_S2K_SALTED:
|
||||
nbytes = 10;
|
||||
break;
|
||||
case CDK_S2K_ITERSALTED:
|
||||
nbytes = 11;
|
||||
break;
|
||||
}
|
||||
nbytes += sk->protect.ivlen;
|
||||
nbytes++; /* single cipher byte */
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
write_secret_key (cdk_stream_t out, cdk_pkt_seckey_t sk,
|
||||
int is_subkey, int old_ctb)
|
||||
{
|
||||
cdk_pkt_pubkey_t pk = NULL;
|
||||
size_t size = 6, npkey, nskey;
|
||||
int pkttype, s2k_mode;
|
||||
cdk_error_t rc;
|
||||
|
||||
assert (out);
|
||||
assert (sk);
|
||||
|
||||
if (!sk->pk)
|
||||
return CDK_Inv_Value;
|
||||
pk = sk->pk;
|
||||
if (pk->version < 2 || pk->version > 4)
|
||||
return CDK_Inv_Packet;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("write_secret_key:\n");
|
||||
|
||||
npkey = cdk_pk_get_npkey (pk->pubkey_algo);
|
||||
nskey = cdk_pk_get_nskey (pk->pubkey_algo);
|
||||
if (!npkey || !nskey)
|
||||
return CDK_Inv_Algo;
|
||||
if (pk->version < 4)
|
||||
size += 2;
|
||||
/* If the key is unprotected, the 1 extra byte:
|
||||
1 octet - cipher algorithm byte (0x00)
|
||||
the other bytes depend on the mode:
|
||||
a) simple checksum - 2 octets
|
||||
b) sha-1 checksum - 20 octets */
|
||||
size = !sk->is_protected ? size + 1 : size + 1 + calc_s2ksize (sk);
|
||||
size += calc_mpisize (pk->mpi, npkey);
|
||||
if (sk->version == 3 || !sk->is_protected)
|
||||
{
|
||||
if (sk->version == 3)
|
||||
{
|
||||
size += 2; /* force simple checksum */
|
||||
sk->protect.sha1chk = 0;
|
||||
}
|
||||
else
|
||||
size += sk->protect.sha1chk ? 20 : 2;
|
||||
size += calc_mpisize (sk->mpi, nskey);
|
||||
}
|
||||
else /* We do not know anything about the encrypted mpi's so we
|
||||
treat the data as opaque. */
|
||||
size += sk->enclen;
|
||||
|
||||
pkttype = is_subkey ? CDK_PKT_SECRET_SUBKEY : CDK_PKT_SECRET_KEY;
|
||||
rc = pkt_write_head (out, old_ctb, size, pkttype);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, pk->version);
|
||||
if (!rc)
|
||||
rc = write_32 (out, pk->timestamp);
|
||||
if (!rc && pk->version < 4)
|
||||
{
|
||||
u16 ndays = 0;
|
||||
if (pk->expiredate)
|
||||
ndays = (u16) ((pk->expiredate - pk->timestamp) / 86400L);
|
||||
rc = write_16 (out, ndays);
|
||||
}
|
||||
if (!rc)
|
||||
rc = stream_putc (out, pk->pubkey_algo);
|
||||
if (!rc)
|
||||
rc = write_mpibuf (out, pk->mpi, npkey);
|
||||
if (sk->is_protected == 0)
|
||||
rc = stream_putc (out, 0x00);
|
||||
else
|
||||
{
|
||||
if (is_RSA (pk->pubkey_algo) && pk->version < 4)
|
||||
stream_putc (out, sk->protect.algo);
|
||||
else if (sk->protect.s2k)
|
||||
{
|
||||
s2k_mode = sk->protect.s2k->mode;
|
||||
rc = stream_putc (out, sk->protect.sha1chk ? 0xFE : 0xFF);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, sk->protect.algo);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, sk->protect.s2k->mode);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, sk->protect.s2k->hash_algo);
|
||||
if (!rc && (s2k_mode == 1 || s2k_mode == 3))
|
||||
{
|
||||
rc = stream_write (out, sk->protect.s2k->salt, 8);
|
||||
if (!rc && s2k_mode == 3)
|
||||
rc = stream_putc (out, sk->protect.s2k->count);
|
||||
}
|
||||
}
|
||||
else
|
||||
return CDK_Inv_Value;
|
||||
rc = stream_write (out, sk->protect.iv, sk->protect.ivlen);
|
||||
}
|
||||
if (!rc && sk->is_protected && pk->version == 4)
|
||||
{
|
||||
if (sk->encdata && sk->enclen)
|
||||
rc = stream_write (out, sk->encdata, sk->enclen);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!rc)
|
||||
rc = write_mpibuf (out, sk->mpi, nskey);
|
||||
if (!rc)
|
||||
{
|
||||
if (!sk->csum)
|
||||
sk->csum = _cdk_sk_get_csum (sk);
|
||||
rc = write_16 (out, sk->csum);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
write_compressed (cdk_stream_t out, cdk_pkt_compressed_t cd)
|
||||
{
|
||||
cdk_error_t rc;
|
||||
|
||||
assert (out);
|
||||
assert (cd);
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("packet: write_compressed\n");
|
||||
|
||||
/* Use an old (RFC1991) header for this packet. */
|
||||
rc = pkt_write_head (out, 1, 0, CDK_PKT_COMPRESSED);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, cd->algorithm);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
write_literal (cdk_stream_t out, cdk_pkt_literal_t pt, int old_ctb)
|
||||
{
|
||||
byte buf[BUFSIZE];
|
||||
size_t size;
|
||||
cdk_error_t rc;
|
||||
|
||||
assert (out);
|
||||
assert (pt);
|
||||
|
||||
/* We consider a packet without a body as an invalid packet.
|
||||
At least one octet must be present. */
|
||||
if (!pt->len)
|
||||
return CDK_Inv_Packet;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("write_literal:\n");
|
||||
|
||||
size = 6 + pt->namelen + pt->len;
|
||||
rc = pkt_write_head (out, old_ctb, size, CDK_PKT_LITERAL);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = stream_putc (out, pt->mode);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = stream_putc (out, pt->namelen);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (pt->namelen > 0)
|
||||
rc = stream_write (out, pt->name, pt->namelen);
|
||||
if (!rc)
|
||||
rc = write_32 (out, pt->timestamp);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
while (!cdk_stream_eof (pt->buf) && !rc)
|
||||
{
|
||||
rc = stream_read (pt->buf, buf, DIM (buf), &size);
|
||||
if (!rc)
|
||||
rc = stream_write (out, buf, size);
|
||||
}
|
||||
|
||||
wipemem (buf, sizeof (buf));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
write_onepass_sig (cdk_stream_t out, cdk_pkt_onepass_sig_t sig)
|
||||
{
|
||||
cdk_error_t rc;
|
||||
|
||||
assert (out);
|
||||
assert (sig);
|
||||
|
||||
if (sig->version != 3)
|
||||
return CDK_Inv_Packet;
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("write_onepass_sig:\n");
|
||||
|
||||
rc = pkt_write_head (out, 0, 13, CDK_PKT_ONEPASS_SIG);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, sig->version);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, sig->sig_class);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, sig->digest_algo);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, sig->pubkey_algo);
|
||||
if (!rc)
|
||||
rc = write_32 (out, sig->keyid[0]);
|
||||
if (!rc)
|
||||
rc = write_32 (out, sig->keyid[1]);
|
||||
if (!rc)
|
||||
rc = stream_putc (out, sig->last);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static cdk_error_t
|
||||
write_user_id (cdk_stream_t out, cdk_pkt_userid_t id, int old_ctb,
|
||||
int pkttype)
|
||||
{
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!out || !id)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
if (pkttype == CDK_PKT_ATTRIBUTE)
|
||||
{
|
||||
if (!id->attrib_img)
|
||||
return CDK_Inv_Value;
|
||||
rc =
|
||||
pkt_write_head (out, old_ctb, id->attrib_len + 6, CDK_PKT_ATTRIBUTE);
|
||||
if (rc)
|
||||
return rc;
|
||||
/* Write subpacket part. */
|
||||
stream_putc (out, 255);
|
||||
write_32 (out, id->attrib_len + 1);
|
||||
stream_putc (out, 1);
|
||||
rc = stream_write (out, id->attrib_img, id->attrib_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!id->name)
|
||||
return CDK_Inv_Value;
|
||||
rc = pkt_write_head (out, old_ctb, id->len, CDK_PKT_USER_ID);
|
||||
if (!rc)
|
||||
rc = stream_write (out, id->name, id->len);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cdk_pkt_write:
|
||||
* @out: the output stream handle
|
||||
* @pkt: the packet itself
|
||||
*
|
||||
* Write the contents of @pkt into the @out stream.
|
||||
* Return 0 on success.
|
||||
**/
|
||||
cdk_error_t
|
||||
cdk_pkt_write (cdk_stream_t out, cdk_packet_t pkt)
|
||||
{
|
||||
cdk_error_t rc;
|
||||
|
||||
if (!out || !pkt)
|
||||
return CDK_Inv_Value;
|
||||
|
||||
_cdk_log_debug ("write packet pkttype=%d\n", pkt->pkttype);
|
||||
switch (pkt->pkttype)
|
||||
{
|
||||
case CDK_PKT_LITERAL:
|
||||
rc = write_literal (out, pkt->pkt.literal, pkt->old_ctb);
|
||||
break;
|
||||
case CDK_PKT_ONEPASS_SIG:
|
||||
rc = write_onepass_sig (out, pkt->pkt.onepass_sig);
|
||||
break;
|
||||
case CDK_PKT_MDC:
|
||||
rc = write_mdc (out, pkt->pkt.mdc);
|
||||
break;
|
||||
case CDK_PKT_SYMKEY_ENC:
|
||||
rc = write_symkey_enc (out, pkt->pkt.symkey_enc);
|
||||
break;
|
||||
case CDK_PKT_ENCRYPTED:
|
||||
rc = write_encrypted (out, pkt->pkt.encrypted, pkt->old_ctb);
|
||||
break;
|
||||
case CDK_PKT_ENCRYPTED_MDC:
|
||||
rc = write_encrypted_mdc (out, pkt->pkt.encrypted);
|
||||
break;
|
||||
case CDK_PKT_PUBKEY_ENC:
|
||||
rc = write_pubkey_enc (out, pkt->pkt.pubkey_enc, pkt->old_ctb);
|
||||
break;
|
||||
case CDK_PKT_SIGNATURE:
|
||||
rc = write_signature (out, pkt->pkt.signature, pkt->old_ctb);
|
||||
break;
|
||||
case CDK_PKT_PUBLIC_KEY:
|
||||
rc = write_public_key (out, pkt->pkt.public_key, 0, pkt->old_ctb);
|
||||
break;
|
||||
case CDK_PKT_PUBLIC_SUBKEY:
|
||||
rc = write_public_key (out, pkt->pkt.public_key, 1, pkt->old_ctb);
|
||||
break;
|
||||
case CDK_PKT_COMPRESSED:
|
||||
rc = write_compressed (out, pkt->pkt.compressed);
|
||||
break;
|
||||
case CDK_PKT_SECRET_KEY:
|
||||
rc = write_secret_key (out, pkt->pkt.secret_key, 0, pkt->old_ctb);
|
||||
break;
|
||||
case CDK_PKT_SECRET_SUBKEY:
|
||||
rc = write_secret_key (out, pkt->pkt.secret_key, 1, pkt->old_ctb);
|
||||
break;
|
||||
case CDK_PKT_USER_ID:
|
||||
case CDK_PKT_ATTRIBUTE:
|
||||
rc = write_user_id (out, pkt->pkt.user_id, pkt->old_ctb, pkt->pkttype);
|
||||
break;
|
||||
default:
|
||||
rc = CDK_Inv_Packet;
|
||||
break;
|
||||
}
|
||||
|
||||
if (DEBUG_PKT)
|
||||
_cdk_log_debug ("write_packet rc=%d pkttype=%d\n", rc, pkt->pkttype);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
_cdk_pkt_write2 (cdk_stream_t out, int pkttype, void *pktctx)
|
||||
{
|
||||
cdk_packet_t pkt;
|
||||
cdk_error_t rc;
|
||||
|
||||
rc = cdk_pkt_new (&pkt);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
switch (pkttype)
|
||||
{
|
||||
case CDK_PKT_PUBLIC_KEY:
|
||||
case CDK_PKT_PUBLIC_SUBKEY:
|
||||
pkt->pkt.public_key = pktctx;
|
||||
break;
|
||||
case CDK_PKT_SIGNATURE:
|
||||
pkt->pkt.signature = pktctx;
|
||||
break;
|
||||
case CDK_PKT_SECRET_KEY:
|
||||
case CDK_PKT_SECRET_SUBKEY:
|
||||
pkt->pkt.secret_key = pktctx;
|
||||
break;
|
||||
|
||||
case CDK_PKT_USER_ID:
|
||||
pkt->pkt.user_id = pktctx;
|
||||
break;
|
||||
}
|
||||
pkt->pkttype = pkttype;
|
||||
rc = cdk_pkt_write (out, pkt);
|
||||
cdk_free (pkt);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
cdk_error_t
|
||||
_cdk_pkt_write_fp (FILE * out, cdk_packet_t pkt)
|
||||
{
|
||||
cdk_stream_t so;
|
||||
cdk_error_t rc;
|
||||
|
||||
rc = _cdk_stream_fpopen (out, 1, &so);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = cdk_pkt_write (so, pkt);
|
||||
cdk_stream_close (so);
|
||||
return rc;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
SUBDIRS = .
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src/include \
|
||||
-I$(top_srcdir)/src/daemon/https \
|
||||
-I$(top_srcdir)/src/daemon/https/lgl \
|
||||
-I$(top_srcdir)/src/daemon/https/x509 \
|
||||
-I$(top_srcdir)/src/daemon/https/tls \
|
||||
-I$(top_srcdir)/src/daemon/https/openpgp \
|
||||
-I$(top_srcdir)/src/daemon/https/opencdk \
|
||||
-I$(top_srcdir)/src/daemon/https/minitasn1
|
||||
|
||||
noinst_LTLIBRARIES = libopenpgp.la
|
||||
|
||||
# libopenpgp_la_LDFLAGS = -l$(top_srcdir)/src/daemon/https/libextra/.lib/libextra.la
|
||||
|
||||
libopenpgp_la_SOURCES = \
|
||||
pgp_privkey.c \
|
||||
compat.c \
|
||||
pgp_verify.c \
|
||||
extras.c \
|
||||
pgp.c
|
||||
|
||||
# x libextra source list
|
||||
libopenpgp_la_SOURCES += \
|
||||
gnutls_openpgp.c \
|
||||
gnutls_ia.c \
|
||||
gnutls_extra.c
|
||||
@@ -1,252 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation
|
||||
*
|
||||
* Author: Timo Schulz, Nikos Mavrogiannopoulos
|
||||
*
|
||||
* This file is part of GNUTLS-EXTRA.
|
||||
*
|
||||
* GNUTLS-EXTRA 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.
|
||||
*
|
||||
* GNUTLS-EXTRA is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Compatibility functions on OpenPGP key parsing.
|
||||
*/
|
||||
|
||||
#include <gnutls_int.h>
|
||||
#include <gnutls_errors.h>
|
||||
#include <gnutls_openpgp.h>
|
||||
#include "openpgp.h"
|
||||
|
||||
/*-
|
||||
* gnutls_openpgp_verify_key - Verify all signatures on the key
|
||||
* @cert_list: the structure that holds the certificates.
|
||||
* @cert_list_lenght: the items in the cert_list.
|
||||
* @status: the output of the verification function
|
||||
*
|
||||
* Verify all signatures in the certificate list. When the key
|
||||
* is not available, the signature is skipped.
|
||||
*
|
||||
* The return value is one of the CertificateStatus entries.
|
||||
*
|
||||
* NOTE: this function does not verify using any "web of trust". You
|
||||
* may use GnuPG for that purpose, or any other external PGP application.
|
||||
-*/
|
||||
int
|
||||
_gnutls_openpgp_verify_key (const mhd_gtls_cert_credentials_t cred,
|
||||
const gnutls_datum_t * cert_list,
|
||||
int cert_list_length, unsigned int *status)
|
||||
{
|
||||
int ret = 0;
|
||||
gnutls_openpgp_crt_t key = NULL;
|
||||
unsigned int verify = 0, verify_self = 0;
|
||||
|
||||
if (!cert_list || cert_list_length != 1)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_NO_CERTIFICATE_FOUND;
|
||||
}
|
||||
|
||||
ret = gnutls_openpgp_crt_init (&key);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret =
|
||||
gnutls_openpgp_crt_import (key, &cert_list[0], GNUTLS_OPENPGP_FMT_RAW);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
#ifndef KEYRING_HACK
|
||||
if (cred->keyring != NULL)
|
||||
{
|
||||
ret = gnutls_openpgp_crt_verify_ring (key, cred->keyring, 0, &verify);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
gnutls_openpgp_keyring_t kring;
|
||||
|
||||
ret = gnutls_openpgp_keyring_init (&kring);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret =
|
||||
gnutls_openpgp_keyring_import (kring, &cred->keyring,
|
||||
cred->keyring_format);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
gnutls_openpgp_keyring_deinit (kring);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gnutls_openpgp_crt_verify_ring (key, kring, 0, &verify);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
gnutls_openpgp_keyring_deinit (kring);
|
||||
return ret;
|
||||
}
|
||||
gnutls_openpgp_keyring_deinit (kring);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Now try the self signature. */
|
||||
ret = gnutls_openpgp_crt_verify_self (key, 0, &verify_self);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
goto leave;
|
||||
}
|
||||
|
||||
*status = verify_self | verify;
|
||||
|
||||
#ifndef KEYRING_HACK
|
||||
/* If we only checked the self signature. */
|
||||
if (!cred->keyring)
|
||||
#else
|
||||
if (!cred->keyring.data || !cred->keyring.size)
|
||||
#endif
|
||||
*status |= GNUTLS_CERT_SIGNER_NOT_FOUND;
|
||||
|
||||
|
||||
ret = 0;
|
||||
|
||||
leave:
|
||||
gnutls_openpgp_crt_deinit (key);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*-
|
||||
* gnutls_openpgp_fingerprint - Gets the fingerprint
|
||||
* @cert: the raw data that contains the OpenPGP public key.
|
||||
* @fpr: the buffer to save the fingerprint.
|
||||
* @fprlen: the integer to save the length of the fingerprint.
|
||||
*
|
||||
* Returns the fingerprint of the OpenPGP key. Depence on the algorithm,
|
||||
* the fingerprint can be 16 or 20 bytes.
|
||||
-*/
|
||||
int
|
||||
_gnutls_openpgp_fingerprint (const gnutls_datum_t * cert,
|
||||
unsigned char *fpr, size_t * fprlen)
|
||||
{
|
||||
gnutls_openpgp_crt_t key;
|
||||
int ret;
|
||||
|
||||
ret = gnutls_openpgp_crt_init (&key);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gnutls_openpgp_crt_import (key, cert, GNUTLS_OPENPGP_FMT_RAW);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gnutls_openpgp_crt_get_fingerprint (key, fpr, fprlen);
|
||||
gnutls_openpgp_crt_deinit (key);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-
|
||||
* gnutls_openpgp_get_raw_key_creation_time - Extract the timestamp
|
||||
* @cert: the raw data that contains the OpenPGP public key.
|
||||
*
|
||||
* Returns the timestamp when the OpenPGP key was created.
|
||||
-*/
|
||||
time_t
|
||||
_gnutls_openpgp_get_raw_key_creation_time (const gnutls_datum_t * cert)
|
||||
{
|
||||
gnutls_openpgp_crt_t key;
|
||||
int ret;
|
||||
time_t tim;
|
||||
|
||||
ret = gnutls_openpgp_crt_init (&key);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gnutls_openpgp_crt_import (key, cert, GNUTLS_OPENPGP_FMT_RAW);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
tim = gnutls_openpgp_crt_get_creation_time (key);
|
||||
|
||||
gnutls_openpgp_crt_deinit (key);
|
||||
|
||||
return tim;
|
||||
}
|
||||
|
||||
|
||||
/*-
|
||||
* gnutls_openpgp_get_raw_key_expiration_time - Extract the expire date
|
||||
* @cert: the raw data that contains the OpenPGP public key.
|
||||
*
|
||||
* Returns the time when the OpenPGP key expires. A value of '0' means
|
||||
* that the key doesn't expire at all.
|
||||
-*/
|
||||
time_t
|
||||
_gnutls_openpgp_get_raw_key_expiration_time (const gnutls_datum_t * cert)
|
||||
{
|
||||
gnutls_openpgp_crt_t key;
|
||||
int ret;
|
||||
time_t tim;
|
||||
|
||||
ret = gnutls_openpgp_crt_init (&key);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gnutls_openpgp_crt_import (key, cert, GNUTLS_OPENPGP_FMT_RAW);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
tim = gnutls_openpgp_crt_get_expiration_time (key);
|
||||
|
||||
gnutls_openpgp_crt_deinit (key);
|
||||
|
||||
return tim;
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation
|
||||
*
|
||||
* Author: Nikos Mavrogiannopoulos, Timo Schulz
|
||||
*
|
||||
* This file is part of GNUTLS-EXTRA.
|
||||
*
|
||||
* GNUTLS-EXTRA 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.
|
||||
*
|
||||
* GNUTLS-EXTRA is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Functions on OpenPGP keyring parsing
|
||||
*/
|
||||
|
||||
#include <gnutls_int.h>
|
||||
#include <gnutls_datum.h>
|
||||
#include <gnutls_global.h>
|
||||
#include <gnutls_errors.h>
|
||||
#include <gnutls_openpgp.h>
|
||||
#include <gnutls_num.h>
|
||||
#include "openpgp.h"
|
||||
|
||||
/* Keyring stuff. */
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_keyring_init - This function initializes a gnutls_openpgp_keyring_t structure
|
||||
* @keyring: The structure to be initialized
|
||||
*-
|
||||
* This function will initialize an OpenPGP keyring structure.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*
|
||||
**/
|
||||
int
|
||||
gnutls_openpgp_keyring_init (gnutls_openpgp_keyring_t * keyring)
|
||||
{
|
||||
*keyring = gnutls_calloc (1, sizeof (gnutls_openpgp_keyring_int));
|
||||
|
||||
if (*keyring)
|
||||
return 0; /* success */
|
||||
return GNUTLS_E_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_keyring_deinit - This function deinitializes memory used by a gnutls_openpgp_keyring_t structure
|
||||
* @keyring: The structure to be initialized
|
||||
*
|
||||
* This function will deinitialize a keyring structure.
|
||||
*
|
||||
**/
|
||||
void
|
||||
gnutls_openpgp_keyring_deinit (gnutls_openpgp_keyring_t keyring)
|
||||
{
|
||||
if (!keyring)
|
||||
return;
|
||||
|
||||
if (keyring->db)
|
||||
{
|
||||
cdk_keydb_free (keyring->db);
|
||||
keyring->db = NULL;
|
||||
}
|
||||
|
||||
/* In some cases the stream is also stored outside the keydb context
|
||||
and we need to close it here then. */
|
||||
if (keyring->db_stream)
|
||||
{
|
||||
cdk_stream_close (keyring->db_stream);
|
||||
keyring->db_stream = NULL;
|
||||
}
|
||||
|
||||
gnutls_free (keyring);
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_keyring_check_id - Check if a key id exists in the keyring
|
||||
* @ring: holds the keyring to check against
|
||||
* @keyid: will hold the keyid to check for.
|
||||
* @flags: unused (should be 0)
|
||||
*
|
||||
* Check if a given key ID exists in the keyring.
|
||||
*
|
||||
* Returns 0 on success (if keyid exists) and a negative error code
|
||||
* on failure.
|
||||
**/
|
||||
int
|
||||
gnutls_openpgp_keyring_check_id (gnutls_openpgp_keyring_t ring,
|
||||
const unsigned char keyid[8],
|
||||
unsigned int flags)
|
||||
{
|
||||
cdk_pkt_pubkey_t pk;
|
||||
uint32_t id[2];
|
||||
|
||||
id[0] = mhd_gtls_read_uint32 (keyid);
|
||||
id[1] = mhd_gtls_read_uint32 (&keyid[4]);
|
||||
|
||||
if (!cdk_keydb_get_pk (ring->db, id, &pk))
|
||||
{
|
||||
cdk_pk_release (pk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_gnutls_debug_log ("PGP: key not found %08lX\n", (unsigned long) id[1]);
|
||||
return GNUTLS_E_NO_CERTIFICATE_FOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_keyring_import - Import a raw- or Base64-encoded OpenPGP keyring
|
||||
* @keyring: The structure to store the parsed key.
|
||||
* @data: The RAW or BASE64 encoded keyring.
|
||||
* @format: One of #gnutls_openpgp_keyring_fmt elements.
|
||||
*
|
||||
* This function will convert the given RAW or Base64 encoded keyring to the
|
||||
* native #gnutls_openpgp_keyring_t format. The output will be stored in
|
||||
* 'keyring'.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*
|
||||
**/
|
||||
int
|
||||
gnutls_openpgp_keyring_import (gnutls_openpgp_keyring_t keyring,
|
||||
const gnutls_datum_t * data,
|
||||
gnutls_openpgp_crt_fmt_t format)
|
||||
{
|
||||
cdk_error_t err;
|
||||
cdk_stream_t input;
|
||||
|
||||
_gnutls_debug_log ("PGP: keyring import format '%s'\n",
|
||||
format == GNUTLS_OPENPGP_FMT_RAW ? "raw" : "base64");
|
||||
|
||||
if (format == GNUTLS_OPENPGP_FMT_RAW)
|
||||
{
|
||||
err
|
||||
=
|
||||
cdk_keydb_new (&keyring->db, CDK_DBTYPE_DATA, data->data, data->size);
|
||||
if (err)
|
||||
gnutls_assert ();
|
||||
return _gnutls_map_cdk_rc (err);
|
||||
}
|
||||
|
||||
/* Create a new stream from the given data, which means to
|
||||
allocate a new stream and to write the data in the stream.
|
||||
Then push the armor filter to decode the data and to store
|
||||
it in the raw format. */
|
||||
err = cdk_stream_tmp_from_mem (data->data, data->size, &input);
|
||||
if (!err)
|
||||
err = cdk_stream_set_armor_flag (input, 0);
|
||||
if (!err)
|
||||
err = cdk_keydb_new_from_stream (&keyring->db, 0, input);
|
||||
if (err)
|
||||
{
|
||||
cdk_stream_close (input);
|
||||
gnutls_assert ();
|
||||
}
|
||||
else
|
||||
/* The keydb function will not close the stream itself, so we need to
|
||||
store it separately to close it later. */
|
||||
keyring->db_stream = input;
|
||||
|
||||
return _gnutls_map_cdk_rc (err);
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2001, 2004, 2005, 2007 Free Software Foundation
|
||||
*
|
||||
* Author: Nikos Mavrogiannopoulos
|
||||
*
|
||||
* This file is part of GNUTLS-EXTRA.
|
||||
*
|
||||
* GNUTLS-EXTRA 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.
|
||||
*
|
||||
* GNUTLS-EXTRA is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <gnutls_int.h>
|
||||
#include <gnutls_errors.h>
|
||||
#include <gnutls_extensions.h>
|
||||
#include <gnutls_openpgp.h>
|
||||
#include <gnutls_extra.h>
|
||||
#include <gnutls_extra_hooks.h>
|
||||
#include <gnutls_algorithms.h>
|
||||
|
||||
/* the number of the compression algorithms available in the compression
|
||||
* structure.
|
||||
*/
|
||||
extern int _gnutls_comp_algorithms_size;
|
||||
|
||||
/* Functions in gnutls that have not been initialized.
|
||||
*/
|
||||
|
||||
static int _gnutls_init_extra = 0;
|
||||
|
||||
/**
|
||||
* gnutls_global_init_extra - This function initializes the global state of gnutls-extra
|
||||
*
|
||||
* This function initializes the global state of gnutls-extra library
|
||||
* to defaults. Returns zero on success.
|
||||
*
|
||||
* Note that MHD_gnutls_global_init() has to be called before this
|
||||
* function. If this function is not called then the gnutls-extra
|
||||
* library will not be usable.
|
||||
*
|
||||
**/
|
||||
int
|
||||
gnutls_global_init_extra (void)
|
||||
{
|
||||
/* If the version of libgnutls != version of
|
||||
* libextra, then do not initialize the library.
|
||||
* This is because it may break things.
|
||||
*/
|
||||
_gnutls_init_extra++;
|
||||
|
||||
if (_gnutls_init_extra != 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Register the openpgp functions. This is because some
|
||||
* of them are defined to be NULL in the main library.
|
||||
*/
|
||||
_gnutls_add_openpgp_functions (_gnutls_openpgp_verify_key,
|
||||
_gnutls_openpgp_get_raw_key_creation_time,
|
||||
_gnutls_openpgp_get_raw_key_expiration_time,
|
||||
_gnutls_openpgp_fingerprint,
|
||||
_gnutls_openpgp_request_key,
|
||||
_gnutls_openpgp_raw_key_to_gcert,
|
||||
_gnutls_openpgp_raw_privkey_to_gkey,
|
||||
_gnutls_openpgp_crt_to_gcert,
|
||||
_gnutls_openpgp_privkey_to_gkey,
|
||||
gnutls_openpgp_crt_deinit,
|
||||
gnutls_openpgp_keyring_deinit,
|
||||
gnutls_openpgp_privkey_deinit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <strverscmp.h>
|
||||
|
||||
/**
|
||||
* gnutls_extra_check_version - This function checks the library's version
|
||||
* @req_version: the version to check
|
||||
*
|
||||
* Check that the version of the gnutls-extra library is at minimum
|
||||
* the requested one and return the version string; return NULL if the
|
||||
* condition is not satisfied. If a NULL is passed to this function,
|
||||
* no check is done, but the version string is simply returned.
|
||||
*
|
||||
**/
|
||||
const char *
|
||||
gnutls_extra_check_version (const char *req_version)
|
||||
{
|
||||
if (!req_version || strverscmp (req_version, VERSION) <= 0)
|
||||
return VERSION;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation
|
||||
*
|
||||
* Author: Nikos Mavrogiannopoulos
|
||||
*
|
||||
* This file is part of GNUTLS-EXTRA.
|
||||
*
|
||||
* GNUTLS-EXTRA 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.
|
||||
*
|
||||
* GNUTLS-EXTRA 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 GNUTLS-EXTRA; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <auth_cert.h>
|
||||
|
||||
typedef int (*OPENPGP_VERIFY_KEY_FUNC) (const
|
||||
mhd_gtls_cert_credentials_t,
|
||||
const gnutls_datum_t *, int,
|
||||
unsigned int *);
|
||||
|
||||
typedef time_t (*OPENPGP_KEY_CREATION_TIME_FUNC) (const gnutls_datum_t *);
|
||||
typedef time_t (*OPENPGP_KEY_EXPIRATION_TIME_FUNC) (const gnutls_datum_t *);
|
||||
typedef int (*OPENPGP_KEY_REQUEST) (mhd_gtls_session_t, gnutls_datum_t *,
|
||||
const mhd_gtls_cert_credentials_t,
|
||||
opaque *, int);
|
||||
|
||||
typedef int (*OPENPGP_FINGERPRINT) (const gnutls_datum_t *,
|
||||
unsigned char *, size_t *);
|
||||
|
||||
typedef int (*OPENPGP_RAW_KEY_TO_GCERT) (gnutls_cert *,
|
||||
const gnutls_datum_t *);
|
||||
typedef int (*OPENPGP_RAW_PRIVKEY_TO_GKEY) (gnutls_privkey *,
|
||||
const gnutls_datum_t *);
|
||||
|
||||
typedef int (*OPENPGP_KEY_TO_GCERT) (gnutls_cert *, gnutls_openpgp_crt_t);
|
||||
typedef int (*OPENPGP_PRIVKEY_TO_GKEY) (gnutls_privkey *,
|
||||
gnutls_openpgp_privkey_t);
|
||||
typedef void (*OPENPGP_KEY_DEINIT) (gnutls_openpgp_crt_t);
|
||||
typedef void (*OPENPGP_PRIVKEY_DEINIT) (gnutls_openpgp_privkey_t);
|
||||
@@ -1,901 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2005, 2006 Free Software Foundation
|
||||
*
|
||||
* Author: Simon Josefsson
|
||||
*
|
||||
* This file is part of GNUTLS-EXTRA.
|
||||
*
|
||||
* GNUTLS-EXTRA 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.
|
||||
*
|
||||
* GNUTLS-EXTRA is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gnutls_int.h"
|
||||
#include "gnutls_record.h"
|
||||
#include "gnutls_errors.h"
|
||||
#include "gnutls_num.h"
|
||||
#include "gnutls_state.h"
|
||||
|
||||
#define CHECKSUM_SIZE 12
|
||||
|
||||
struct gnutls_ia_client_credentials_st
|
||||
{
|
||||
gnutls_ia_avp_func avp_func;
|
||||
void *avp_ptr;
|
||||
};
|
||||
|
||||
struct gnutls_ia_server_credentials_st
|
||||
{
|
||||
gnutls_ia_avp_func avp_func;
|
||||
void *avp_ptr;
|
||||
};
|
||||
|
||||
static const char server_finished_label[] = "server phase finished";
|
||||
static const char client_finished_label[] = "client phase finished";
|
||||
static const char inner_permutation_label[] = "inner secret permutation";
|
||||
static const char challenge_label[] = "inner application challenge";
|
||||
|
||||
/*
|
||||
* The TLS/IA packet is the InnerApplication token, described as
|
||||
* follows in draft-funk-tls-inner-application-extension-01.txt:
|
||||
*
|
||||
* enum {
|
||||
* application_payload(0), intermediate_phase_finished(1),
|
||||
* final_phase_finished(2), (255)
|
||||
* } InnerApplicationType;
|
||||
*
|
||||
* struct {
|
||||
* InnerApplicationType msg_type;
|
||||
* uint24 length;
|
||||
* select (InnerApplicationType) {
|
||||
* case application_payload: ApplicationPayload;
|
||||
* case intermediate_phase_finished: IntermediatePhaseFinished;
|
||||
* case final_phase_finished: FinalPhaseFinished;
|
||||
* } body;
|
||||
* } InnerApplication;
|
||||
*
|
||||
*/
|
||||
|
||||
/* Send TLS/IA data. If data==NULL && sizeofdata==NULL, then the last
|
||||
send was interrupted for some reason, and then we try to send it
|
||||
again. Returns the number of bytes sent, or an error code. If
|
||||
this return E_AGAIN and E_INTERRUPTED, call this function again
|
||||
with data==NULL&&sizeofdata=0NULL until it returns successfully. */
|
||||
static ssize_t
|
||||
_gnutls_send_inner_application (mhd_gtls_session_t session,
|
||||
gnutls_ia_apptype_t msg_type,
|
||||
const char *data, size_t sizeofdata)
|
||||
{
|
||||
opaque *p = NULL;
|
||||
size_t plen = 0;
|
||||
ssize_t len;
|
||||
|
||||
if (data != NULL)
|
||||
{
|
||||
plen = sizeofdata + 4;
|
||||
p = gnutls_malloc (plen);
|
||||
if (!p)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
*(unsigned char *) p = (unsigned char) (msg_type & 0xFF);
|
||||
mhd_gtls_write_uint24 (sizeofdata, p + 1);
|
||||
memcpy (p + 4, data, sizeofdata);
|
||||
}
|
||||
|
||||
len = mhd_gtls_send_int (session, GNUTLS_INNER_APPLICATION, -1, p, plen);
|
||||
|
||||
if (p)
|
||||
gnutls_free (p);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Receive TLS/IA data. Store received TLS/IA message type in
|
||||
*MSG_TYPE, and the data in DATA of max SIZEOFDATA size. Return the
|
||||
number of bytes read, or an error code. */
|
||||
static ssize_t
|
||||
_gnutls_recv_inner_application (mhd_gtls_session_t session,
|
||||
gnutls_ia_apptype_t * msg_type,
|
||||
opaque * data, size_t sizeofdata)
|
||||
{
|
||||
ssize_t len;
|
||||
opaque pkt[4];
|
||||
|
||||
len = mhd_gtls_recv_int (session, GNUTLS_INNER_APPLICATION, -1, pkt, 4);
|
||||
if (len != 4)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
|
||||
}
|
||||
|
||||
*msg_type = pkt[0];
|
||||
len = mhd_gtls_read_uint24 (&pkt[1]);
|
||||
|
||||
if (*msg_type != GNUTLS_IA_APPLICATION_PAYLOAD && len != CHECKSUM_SIZE)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
|
||||
}
|
||||
|
||||
if (sizeofdata < len)
|
||||
{
|
||||
/* XXX push back pkt to IA buffer? */
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_SHORT_MEMORY_BUFFER;
|
||||
}
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
int tmplen = len;
|
||||
|
||||
len = mhd_gtls_recv_int (session, GNUTLS_INNER_APPLICATION, -1,
|
||||
data, tmplen);
|
||||
if (len != tmplen)
|
||||
{
|
||||
gnutls_assert ();
|
||||
/* XXX Correct? */
|
||||
return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Apply the TLS PRF using the TLS/IA inner secret as keying material,
|
||||
where the seed is the client random concatenated with the server
|
||||
random concatenated EXTRA of EXTRA_SIZE length (which can be NULL/0
|
||||
respectively). LABEL and LABEL_SIZE is used as the label. The
|
||||
result is placed in pre-allocated OUT of OUTSIZE length. */
|
||||
static int
|
||||
_gnutls_ia_prf (mhd_gtls_session_t session,
|
||||
size_t label_size,
|
||||
const char *label,
|
||||
size_t extra_size,
|
||||
const char *extra, size_t outsize, opaque * out)
|
||||
{
|
||||
int ret;
|
||||
opaque *seed;
|
||||
size_t seedsize = 2 * TLS_RANDOM_SIZE + extra_size;
|
||||
|
||||
seed = gnutls_malloc (seedsize);
|
||||
if (!seed)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
memcpy (seed, session->security_parameters.server_random, TLS_RANDOM_SIZE);
|
||||
memcpy (seed + TLS_RANDOM_SIZE, session->security_parameters.client_random,
|
||||
TLS_RANDOM_SIZE);
|
||||
memcpy (seed + 2 * TLS_RANDOM_SIZE, extra, extra_size);
|
||||
|
||||
ret = mhd_gtls_PRF (session, session->security_parameters.inner_secret,
|
||||
TLS_MASTER_SIZE,
|
||||
label, label_size, seed, seedsize, outsize, out);
|
||||
|
||||
gnutls_free (seed);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_ia_permute_inner_secret:
|
||||
* @session: is a #mhd_gtls_session_t structure.
|
||||
* @session_keys_size: Size of generated session keys (0 if none).
|
||||
* @session_keys: Generated session keys, used to permute inner secret
|
||||
* (NULL if none).
|
||||
*
|
||||
* Permute the inner secret using the generated session keys.
|
||||
*
|
||||
* This can be called in the TLS/IA AVP callback to mix any generated
|
||||
* session keys with the TLS/IA inner secret.
|
||||
*
|
||||
* Return value: Return zero on success, or a negative error code.
|
||||
**/
|
||||
int
|
||||
gnutls_ia_permute_inner_secret (mhd_gtls_session_t session,
|
||||
size_t session_keys_size,
|
||||
const char *session_keys)
|
||||
{
|
||||
return _gnutls_ia_prf (session,
|
||||
sizeof (inner_permutation_label) - 1,
|
||||
inner_permutation_label,
|
||||
session_keys_size,
|
||||
session_keys,
|
||||
TLS_RANDOM_SIZE,
|
||||
session->security_parameters.inner_secret);
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_ia_generate_challenge:
|
||||
* @session: is a #mhd_gtls_session_t structure.
|
||||
* @buffer_size: size of output buffer.
|
||||
* @buffer: pre-allocated buffer to contain @buffer_size bytes of output.
|
||||
*
|
||||
* Generate an application challenge that the client cannot control or
|
||||
* predict, based on the TLS/IA inner secret.
|
||||
*
|
||||
* Return value: Returns 0 on success, or an negative error code.
|
||||
**/
|
||||
int
|
||||
gnutls_ia_generate_challenge (mhd_gtls_session_t session,
|
||||
size_t buffer_size, char *buffer)
|
||||
{
|
||||
return _gnutls_ia_prf (session,
|
||||
sizeof (challenge_label) - 1,
|
||||
challenge_label, 0, NULL, buffer_size, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_ia_extract_inner_secret:
|
||||
* @session: is a #mhd_gtls_session_t structure.
|
||||
* @buffer: pre-allocated buffer to hold 48 bytes of inner secret.
|
||||
*
|
||||
* Copy the 48 bytes large inner secret into the specified buffer
|
||||
*
|
||||
* This function is typically used after the TLS/IA handshake has
|
||||
* concluded. The TLS/IA inner secret can be used as input to a PRF
|
||||
* to derive session keys. Do not use the inner secret directly as a
|
||||
* session key, because for a resumed session that does not include an
|
||||
* application phase, the inner secret will be identical to the inner
|
||||
* secret in the original session. It is important to include, for
|
||||
* example, the client and server randomness when deriving a sesssion
|
||||
* key from the inner secret.
|
||||
**/
|
||||
void
|
||||
gnutls_ia_extract_inner_secret (mhd_gtls_session_t session, char *buffer)
|
||||
{
|
||||
memcpy (buffer, session->security_parameters.inner_secret, TLS_MASTER_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_ia_endphase_send:
|
||||
* @session: is a #mhd_gtls_session_t structure.
|
||||
* @final_p: Set iff this should signal the final phase.
|
||||
*
|
||||
* Send a TLS/IA end phase message.
|
||||
*
|
||||
* In the client, this should only be used to acknowledge an end phase
|
||||
* message sent by the server.
|
||||
*
|
||||
* In the server, this can be called instead of gnutls_ia_send() if
|
||||
* the server wishes to end an application phase.
|
||||
*
|
||||
* Return value: Return 0 on success, or an error code.
|
||||
**/
|
||||
int
|
||||
gnutls_ia_endphase_send (mhd_gtls_session_t session, int final_p)
|
||||
{
|
||||
opaque local_checksum[CHECKSUM_SIZE];
|
||||
int client = session->security_parameters.entity == GNUTLS_CLIENT;
|
||||
const char *label = client ? client_finished_label : server_finished_label;
|
||||
int size_of_label = client ? sizeof (client_finished_label) :
|
||||
sizeof (server_finished_label);
|
||||
ssize_t len;
|
||||
int ret;
|
||||
|
||||
ret = mhd_gtls_PRF (session, session->security_parameters.inner_secret,
|
||||
TLS_MASTER_SIZE, label, size_of_label - 1,
|
||||
/* XXX specification unclear on seed. */
|
||||
"", 0, CHECKSUM_SIZE, local_checksum);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
len = _gnutls_send_inner_application
|
||||
(session,
|
||||
final_p ? GNUTLS_IA_FINAL_PHASE_FINISHED :
|
||||
GNUTLS_IA_INTERMEDIATE_PHASE_FINISHED, local_checksum, CHECKSUM_SIZE);
|
||||
|
||||
/* XXX Instead of calling this function over and over...?
|
||||
* while (len == GNUTLS_E_AGAIN || len == GNUTLS_E_INTERRUPTED)
|
||||
* len = mhd_gtls_io_write_flush(session);
|
||||
*/
|
||||
|
||||
if (len < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_ia_verify_endphase:
|
||||
* @session: is a #mhd_gtls_session_t structure.
|
||||
* @checksum: 12-byte checksum data, received from gnutls_ia_recv().
|
||||
*
|
||||
* Verify TLS/IA end phase checksum data. If verification fails, the
|
||||
* %GNUTLS_A_INNER_APPLICATION_VERIFICATION alert is sent to the other
|
||||
* sie.
|
||||
*
|
||||
* This function is called when gnutls_ia_recv() return
|
||||
* %GNUTLS_E_WARNING_IA_IPHF_RECEIVED or
|
||||
* %GNUTLS_E_WARNING_IA_FPHF_RECEIVED.
|
||||
*
|
||||
* Return value: Return 0 on successful verification, or an error
|
||||
* code. If the checksum verification of the end phase message fails,
|
||||
* %GNUTLS_E_IA_VERIFY_FAILED is returned.
|
||||
**/
|
||||
int
|
||||
gnutls_ia_verify_endphase (mhd_gtls_session_t session, const char *checksum)
|
||||
{
|
||||
char local_checksum[CHECKSUM_SIZE];
|
||||
int client = session->security_parameters.entity == GNUTLS_CLIENT;
|
||||
const char *label = client ? server_finished_label : client_finished_label;
|
||||
int size_of_label = client ? sizeof (server_finished_label) :
|
||||
sizeof (client_finished_label);
|
||||
int ret;
|
||||
|
||||
ret = mhd_gtls_PRF (session, session->security_parameters.inner_secret,
|
||||
TLS_MASTER_SIZE,
|
||||
label, size_of_label - 1,
|
||||
"", 0, CHECKSUM_SIZE, local_checksum);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (memcmp (local_checksum, checksum, CHECKSUM_SIZE) != 0)
|
||||
{
|
||||
ret = MHD_gnutls_alert_send (session, GNUTLS_AL_FATAL,
|
||||
GNUTLS_A_INNER_APPLICATION_VERIFICATION);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
return GNUTLS_E_IA_VERIFY_FAILED;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_ia_send: Send peer the specified TLS/IA data.
|
||||
* @session: is a #mhd_gtls_session_t structure.
|
||||
* @data: contains the data to send
|
||||
* @sizeofdata: is the length of the data
|
||||
*
|
||||
* Send TLS/IA application payload data. This function has the
|
||||
* similar semantics with send(). The only difference is that is
|
||||
* accepts a GNUTLS session, and uses different error codes.
|
||||
*
|
||||
* The TLS/IA protocol is synchronous, so you cannot send more than
|
||||
* one packet at a time. The client always send the first packet.
|
||||
*
|
||||
* To finish an application phase in the server, use
|
||||
* gnutls_ia_endphase_send(). The client cannot end an application
|
||||
* phase unilaterally; rather, a client is required to respond with an
|
||||
* endphase of its own if gnutls_ia_recv indicates that the server has
|
||||
* sent one.
|
||||
*
|
||||
* If the EINTR is returned by the internal push function (the default
|
||||
* is send()} then %GNUTLS_E_INTERRUPTED will be returned. If
|
||||
* %GNUTLS_E_INTERRUPTED or %GNUTLS_E_AGAIN is returned, you must call
|
||||
* this function again, with the same parameters; alternatively you
|
||||
* could provide a %NULL pointer for data, and 0 for size.
|
||||
*
|
||||
* Returns the number of bytes sent, or a negative error code.
|
||||
**/
|
||||
ssize_t
|
||||
gnutls_ia_send (mhd_gtls_session_t session, const char *data, size_t sizeofdata)
|
||||
{
|
||||
ssize_t len;
|
||||
|
||||
len = _gnutls_send_inner_application (session,
|
||||
GNUTLS_IA_APPLICATION_PAYLOAD,
|
||||
data, sizeofdata);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_ia_recv - read data from the TLS/IA protocol
|
||||
* @session: is a #mhd_gtls_session_t structure.
|
||||
* @data: the buffer that the data will be read into, must hold >= 12 bytes.
|
||||
* @sizeofdata: the number of requested bytes, must be >= 12.
|
||||
*
|
||||
* Receive TLS/IA data. This function has the similar semantics with
|
||||
* recv(). The only difference is that is accepts a GNUTLS session,
|
||||
* and uses different error codes.
|
||||
*
|
||||
* If the server attempt to finish an application phase, this function
|
||||
* will return %GNUTLS_E_WARNING_IA_IPHF_RECEIVED or
|
||||
* %GNUTLS_E_WARNING_IA_FPHF_RECEIVED. The caller should then invoke
|
||||
* gnutls_ia_verify_endphase(), and if it runs the client side, also
|
||||
* send an endphase message of its own using gnutls_ia_endphase_send.
|
||||
*
|
||||
* If EINTR is returned by the internal push function (the default is
|
||||
* @code{recv()}) then GNUTLS_E_INTERRUPTED will be returned. If
|
||||
* GNUTLS_E_INTERRUPTED or GNUTLS_E_AGAIN is returned, you must call
|
||||
* this function again, with the same parameters; alternatively you
|
||||
* could provide a NULL pointer for data, and 0 for size.
|
||||
*
|
||||
* Returns the number of bytes received. A negative error code is
|
||||
* returned in case of an error. The
|
||||
* %GNUTLS_E_WARNING_IA_IPHF_RECEIVED and
|
||||
* %GNUTLS_E_WARNING_IA_FPHF_RECEIVED errors are returned when an
|
||||
* application phase finished message has been sent by the server.
|
||||
**/
|
||||
ssize_t
|
||||
gnutls_ia_recv (mhd_gtls_session_t session, char *data, size_t sizeofdata)
|
||||
{
|
||||
gnutls_ia_apptype_t msg_type;
|
||||
ssize_t len;
|
||||
|
||||
len = _gnutls_recv_inner_application (session, &msg_type, data, sizeofdata);
|
||||
|
||||
if (msg_type == GNUTLS_IA_INTERMEDIATE_PHASE_FINISHED)
|
||||
return GNUTLS_E_WARNING_IA_IPHF_RECEIVED;
|
||||
else if (msg_type == GNUTLS_IA_FINAL_PHASE_FINISHED)
|
||||
return GNUTLS_E_WARNING_IA_FPHF_RECEIVED;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int
|
||||
_gnutls_ia_client_handshake (mhd_gtls_session_t session)
|
||||
{
|
||||
char *buf = NULL;
|
||||
size_t buflen = 0;
|
||||
char tmp[1024]; /* XXX */
|
||||
ssize_t len;
|
||||
int ret;
|
||||
const struct gnutls_ia_client_credentials_st *cred =
|
||||
mhd_gtls_get_cred (session->key, MHD_GNUTLS_CRD_IA, NULL);
|
||||
|
||||
if (cred == NULL)
|
||||
return GNUTLS_E_INTERNAL_ERROR;
|
||||
|
||||
while (1)
|
||||
{
|
||||
char *avp;
|
||||
size_t avplen;
|
||||
|
||||
ret = cred->avp_func (session, cred->avp_ptr,
|
||||
buf, buflen, &avp, &avplen);
|
||||
if (ret)
|
||||
{
|
||||
int tmpret;
|
||||
tmpret = MHD_gnutls_alert_send (session, GNUTLS_AL_FATAL,
|
||||
GNUTLS_A_INNER_APPLICATION_FAILURE);
|
||||
if (tmpret < 0)
|
||||
gnutls_assert ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
len = gnutls_ia_send (session, avp, avplen);
|
||||
gnutls_free (avp);
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
||||
len = gnutls_ia_recv (session, tmp, sizeof (tmp));
|
||||
if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED ||
|
||||
len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED)
|
||||
{
|
||||
ret = gnutls_ia_verify_endphase (session, tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = gnutls_ia_endphase_send
|
||||
(session, len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED)
|
||||
{
|
||||
buf = NULL;
|
||||
buflen = 0;
|
||||
continue;
|
||||
}
|
||||
else if (len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED)
|
||||
break;
|
||||
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
||||
buflen = len;
|
||||
buf = tmp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_gnutls_ia_server_handshake (mhd_gtls_session_t session)
|
||||
{
|
||||
gnutls_ia_apptype_t msg_type;
|
||||
ssize_t len;
|
||||
char buf[1024];
|
||||
int ret;
|
||||
const struct gnutls_ia_server_credentials_st *cred =
|
||||
mhd_gtls_get_cred (session->key, MHD_GNUTLS_CRD_IA, NULL);
|
||||
|
||||
if (cred == NULL)
|
||||
return GNUTLS_E_INTERNAL_ERROR;
|
||||
|
||||
do
|
||||
{
|
||||
char *avp;
|
||||
size_t avplen;
|
||||
|
||||
len = gnutls_ia_recv (session, buf, sizeof (buf));
|
||||
if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED ||
|
||||
len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED)
|
||||
{
|
||||
ret = gnutls_ia_verify_endphase (session, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (len == GNUTLS_E_WARNING_IA_IPHF_RECEIVED)
|
||||
continue;
|
||||
else if (len == GNUTLS_E_WARNING_IA_FPHF_RECEIVED)
|
||||
break;
|
||||
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
||||
avp = NULL;
|
||||
avplen = 0;
|
||||
|
||||
ret = cred->avp_func (session, cred->avp_ptr, buf, len, &avp, &avplen);
|
||||
if (ret < 0)
|
||||
{
|
||||
int tmpret;
|
||||
tmpret = MHD_gnutls_alert_send (session, GNUTLS_AL_FATAL,
|
||||
GNUTLS_A_INNER_APPLICATION_FAILURE);
|
||||
if (tmpret < 0)
|
||||
gnutls_assert ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
msg_type = ret;
|
||||
|
||||
if (msg_type != GNUTLS_IA_APPLICATION_PAYLOAD)
|
||||
{
|
||||
ret = gnutls_ia_endphase_send (session, msg_type ==
|
||||
GNUTLS_IA_FINAL_PHASE_FINISHED);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
len = gnutls_ia_send (session, avp, avplen);
|
||||
gnutls_free (avp);
|
||||
if (len < 0)
|
||||
return len;
|
||||
}
|
||||
}
|
||||
while (1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_ia_handshake_p:
|
||||
* @session: is a #mhd_gtls_session_t structure.
|
||||
*
|
||||
* Predicate to be used after MHD_gnutls_handshake() to decide whether to
|
||||
* invoke gnutls_ia_handshake(). Usable by both clients and servers.
|
||||
*
|
||||
* Return value: non-zero if TLS/IA handshake is expected, zero
|
||||
* otherwise.
|
||||
**/
|
||||
int
|
||||
gnutls_ia_handshake_p (mhd_gtls_session_t session)
|
||||
{
|
||||
mhd_gtls_ext_st *ext = &session->security_parameters.extensions;
|
||||
|
||||
/* Either local side or peer doesn't do TLS/IA: don't do IA */
|
||||
|
||||
if (!ext->gnutls_ia_enable || !ext->gnutls_ia_peer_enable)
|
||||
return 0;
|
||||
|
||||
/* Not resuming or we don't allow skipping on resumption locally: do IA */
|
||||
|
||||
if (!ext->gnutls_ia_allowskip || !MHD_gtls_session_is_resumed (session))
|
||||
return 1;
|
||||
|
||||
/* If we're resuming and we and the peer both allow skipping on resumption:
|
||||
* don't do IA */
|
||||
|
||||
return !ext->gnutls_ia_peer_allowskip;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gnutls_ia_handshake:
|
||||
* @session: is a #mhd_gtls_session_t structure.
|
||||
*
|
||||
* Perform a TLS/IA handshake. This should be called after
|
||||
* MHD_gnutls_handshake() iff gnutls_ia_handshake_p().
|
||||
*
|
||||
* Return 0 on success, or an error code.
|
||||
**/
|
||||
int
|
||||
gnutls_ia_handshake (mhd_gtls_session_t session)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (session->security_parameters.entity == GNUTLS_CLIENT)
|
||||
ret = _gnutls_ia_client_handshake (session);
|
||||
else
|
||||
ret = _gnutls_ia_server_handshake (session);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_ia_allocate_client_credentials - Used to allocate an gnutls_ia_server_credentials_t structure
|
||||
* @sc: is a pointer to an #gnutls_ia_server_credentials_t structure.
|
||||
*
|
||||
* This structure is complex enough to manipulate directly thus this
|
||||
* helper function is provided in order to allocate it.
|
||||
*
|
||||
* Adding this credential to a session will enable TLS/IA, and will
|
||||
* require an Application Phase after the TLS handshake (if the server
|
||||
* support TLS/IA). Use gnutls_ia_require_inner_phase() to toggle the
|
||||
* TLS/IA mode.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
**/
|
||||
int
|
||||
gnutls_ia_allocate_client_credentials (gnutls_ia_client_credentials_t * sc)
|
||||
{
|
||||
*sc = gnutls_calloc (1, sizeof (**sc));
|
||||
|
||||
if (*sc == NULL)
|
||||
return GNUTLS_E_MEMORY_ERROR;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_ia_free_client_credentials - Used to free an allocated #gnutls_ia_client_credentials_t structure
|
||||
* @sc: is an #gnutls_ia_client_credentials_t structure.
|
||||
*
|
||||
* This structure is complex enough to manipulate directly thus this
|
||||
* helper function is provided in order to free (deallocate) it.
|
||||
*
|
||||
**/
|
||||
void
|
||||
gnutls_ia_free_client_credentials (gnutls_ia_client_credentials_t sc)
|
||||
{
|
||||
gnutls_free (sc);
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_ia_set_client_avp_function - Used to set a AVP callback
|
||||
* @cred: is a #gnutls_ia_client_credentials_t structure.
|
||||
* @avp_func: is the callback function
|
||||
*
|
||||
* Set the TLS/IA AVP callback handler used for the session.
|
||||
*
|
||||
* The AVP callback is called to process AVPs received from the
|
||||
* server, and to get a new AVP to send to the server.
|
||||
*
|
||||
* The callback's function form is:
|
||||
* int (*avp_func) (mhd_gtls_session_t session, void *ptr,
|
||||
* const char *last, size_t lastlen,
|
||||
* char **next, size_t *nextlen);
|
||||
*
|
||||
* The @session parameter is the #mhd_gtls_session_t structure
|
||||
* corresponding to the current session. The @ptr parameter is the
|
||||
* application hook pointer, set through
|
||||
* gnutls_ia_set_client_avp_ptr(). The AVP received from the server
|
||||
* is present in @last of @lastlen size, which will be %NULL on the
|
||||
* first invocation. The newly allocated output AVP to send to the
|
||||
* server should be placed in *@next of *@nextlen size.
|
||||
*
|
||||
* The callback may invoke gnutls_ia_permute_inner_secret() to mix any
|
||||
* generated session keys with the TLS/IA inner secret.
|
||||
*
|
||||
* Return 0 (%GNUTLS_IA_APPLICATION_PAYLOAD) on success, or a negative
|
||||
* error code to abort the TLS/IA handshake.
|
||||
*
|
||||
* Note that the callback must use allocate the @next parameter using
|
||||
* gnutls_malloc(), because it is released via gnutls_free() by the
|
||||
* TLS/IA handshake function.
|
||||
*
|
||||
**/
|
||||
void
|
||||
gnutls_ia_set_client_avp_function (gnutls_ia_client_credentials_t cred,
|
||||
gnutls_ia_avp_func avp_func)
|
||||
{
|
||||
cred->avp_func = avp_func;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_ia_set_client_avp_ptr - Sets a pointer to be sent to TLS/IA callback
|
||||
* @cred: is a #gnutls_ia_client_credentials_t structure.
|
||||
* @ptr: is the pointer
|
||||
*
|
||||
* Sets the pointer that will be provided to the TLS/IA callback
|
||||
* function as the first argument.
|
||||
*
|
||||
**/
|
||||
void
|
||||
gnutls_ia_set_client_avp_ptr (gnutls_ia_client_credentials_t cred, void *ptr)
|
||||
{
|
||||
cred->avp_ptr = ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_ia_get_client_avp_ptr - Returns the pointer which is sent to TLS/IA callback
|
||||
* @cred: is a #gnutls_ia_client_credentials_t structure.
|
||||
*
|
||||
* Returns the pointer that will be provided to the TLS/IA callback
|
||||
* function as the first argument.
|
||||
*
|
||||
**/
|
||||
void *
|
||||
gnutls_ia_get_client_avp_ptr (gnutls_ia_client_credentials_t cred)
|
||||
{
|
||||
return cred->avp_ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_ia_allocate_server_credentials - Used to allocate an gnutls_ia_server_credentials_t structure
|
||||
* @sc: is a pointer to an #gnutls_ia_server_credentials_t structure.
|
||||
*
|
||||
* This structure is complex enough to manipulate directly thus this
|
||||
* helper function is provided in order to allocate it.
|
||||
*
|
||||
* Adding this credential to a session will enable TLS/IA, and will
|
||||
* require an Application Phase after the TLS handshake (if the client
|
||||
* support TLS/IA). Use gnutls_ia_require_inner_phase() to toggle the
|
||||
* TLS/IA mode.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
**/
|
||||
int
|
||||
gnutls_ia_allocate_server_credentials (gnutls_ia_server_credentials_t * sc)
|
||||
{
|
||||
*sc = gnutls_calloc (1, sizeof (**sc));
|
||||
|
||||
if (*sc == NULL)
|
||||
return GNUTLS_E_MEMORY_ERROR;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_ia_free_server_credentials - Used to free an allocated #gnutls_ia_server_credentials_t structure
|
||||
* @sc: is an #gnutls_ia_server_credentials_t structure.
|
||||
*
|
||||
* This structure is complex enough to manipulate directly thus this
|
||||
* helper function is provided in order to free (deallocate) it.
|
||||
*
|
||||
**/
|
||||
void
|
||||
gnutls_ia_free_server_credentials (gnutls_ia_server_credentials_t sc)
|
||||
{
|
||||
gnutls_free (sc);
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_ia_set_server_credentials_function - Used to set a AVP callback
|
||||
* @cred: is a #gnutls_ia_server_credentials_t structure.
|
||||
* @func: is the callback function
|
||||
*
|
||||
* Set the TLS/IA AVP callback handler used for the session.
|
||||
*
|
||||
* The callback's function form is:
|
||||
* int (*avp_func) (mhd_gtls_session_t session, void *ptr,
|
||||
* const char *last, size_t lastlen,
|
||||
* char **next, size_t *nextlen);
|
||||
*
|
||||
* The @session parameter is the #mhd_gtls_session_t structure
|
||||
* corresponding to the current session. The @ptr parameter is the
|
||||
* application hook pointer, set through
|
||||
* gnutls_ia_set_server_avp_ptr(). The AVP received from the client
|
||||
* is present in @last of @lastlen size. The newly allocated output
|
||||
* AVP to send to the client should be placed in *@next of *@nextlen
|
||||
* size.
|
||||
*
|
||||
* The AVP callback is called to process incoming AVPs from the
|
||||
* client, and to get a new AVP to send to the client. It can also be
|
||||
* used to instruct the TLS/IA handshake to do go into the
|
||||
* Intermediate or Final phases. It return a negative error code, or
|
||||
* an #gnutls_ia_apptype_t message type.
|
||||
*
|
||||
* The callback may invoke gnutls_ia_permute_inner_secret() to mix any
|
||||
* generated session keys with the TLS/IA inner secret.
|
||||
*
|
||||
* Specifically, return %GNUTLS_IA_APPLICATION_PAYLOAD (0) to send
|
||||
* another AVP to the client, return
|
||||
* %GNUTLS_IA_INTERMEDIATE_PHASE_FINISHED (1) to indicate that an
|
||||
* IntermediatePhaseFinished message should be sent, and return
|
||||
* %GNUTLS_IA_FINAL_PHASE_FINISHED (2) to indicate that an
|
||||
* FinalPhaseFinished message should be sent. In the last two cases,
|
||||
* the contents of the @next and @nextlen parameter is not used.
|
||||
*
|
||||
* Note that the callback must use allocate the @next parameter using
|
||||
* gnutls_malloc(), because it is released via gnutls_free() by the
|
||||
* TLS/IA handshake function.
|
||||
**/
|
||||
void
|
||||
gnutls_ia_set_server_avp_function (gnutls_ia_server_credentials_t cred,
|
||||
gnutls_ia_avp_func avp_func)
|
||||
{
|
||||
cred->avp_func = avp_func;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_ia_set_server_avp_ptr - Sets a pointer to be sent to TLS/IA callback
|
||||
* @cred: is a #gnutls_ia_client_credentials_t structure.
|
||||
* @ptr: is the pointer
|
||||
*
|
||||
* Sets the pointer that will be provided to the TLS/IA callback
|
||||
* function as the first argument.
|
||||
*
|
||||
**/
|
||||
void
|
||||
gnutls_ia_set_server_avp_ptr (gnutls_ia_server_credentials_t cred, void *ptr)
|
||||
{
|
||||
cred->avp_ptr = ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_ia_get_server_avp_ptr - Returns the pointer which is sent to TLS/IA callback
|
||||
* @cred: is a #gnutls_ia_client_credentials_t structure.
|
||||
*
|
||||
* Returns the pointer that will be provided to the TLS/IA callback
|
||||
* function as the first argument.
|
||||
*
|
||||
**/
|
||||
void *
|
||||
gnutls_ia_get_server_avp_ptr (gnutls_ia_server_credentials_t cred)
|
||||
{
|
||||
return cred->avp_ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_ia_enable - Indicate willingness for TLS/IA application phases
|
||||
* @session: is a #mhd_gtls_session_t structure.
|
||||
* @allow_skip_on_resume: non-zero if local party allows to skip the
|
||||
* TLS/IA application phases for a resumed session.
|
||||
*
|
||||
* Specify whether we must advertise support for the TLS/IA extension
|
||||
* during the handshake.
|
||||
*
|
||||
* At the client side, we always advertise TLS/IA if gnutls_ia_enable
|
||||
* was called before the handshake; at the server side, we also
|
||||
* require that the client has advertised that it wants to run TLS/IA
|
||||
* before including the advertisement, as required by the protocol.
|
||||
*
|
||||
* Similarly, at the client side we always advertise that we allow
|
||||
* TLS/IA to be skipped for resumed sessions if @allow_skip_on_resume
|
||||
* is non-zero; at the server side, we also require that the session
|
||||
* is indeed resumable and that the client has also advertised that it
|
||||
* allows TLS/IA to be skipped for resumed sessions.
|
||||
*
|
||||
* After the TLS handshake, call gnutls_ia_handshake_p() to find out
|
||||
* whether both parties agreed to do a TLS/IA handshake, before
|
||||
* calling gnutls_ia_handshake() or one of the lower level gnutls_ia_*
|
||||
* functions.
|
||||
**/
|
||||
void
|
||||
gnutls_ia_enable (mhd_gtls_session_t session, int allow_skip_on_resume)
|
||||
{
|
||||
session->security_parameters.extensions.gnutls_ia_enable = 1;
|
||||
session->security_parameters.extensions.gnutls_ia_allowskip =
|
||||
allow_skip_on_resume;
|
||||
}
|
||||
@@ -1,966 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation
|
||||
*
|
||||
* Author: Timo Schulz
|
||||
*
|
||||
* This file is part of GNUTLS-EXTRA.
|
||||
*
|
||||
* GNUTLS-EXTRA 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.
|
||||
*
|
||||
* GNUTLS-EXTRA is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gnutls_int.h"
|
||||
#include "gnutls_errors.h"
|
||||
#include "gnutls_mpi.h"
|
||||
#include "gnutls_cert.h"
|
||||
#include "gnutls_datum.h"
|
||||
#include "gnutls_global.h"
|
||||
#include "gnutls_openpgp.h"
|
||||
#include "read-file.h"
|
||||
#include <gnutls_str.h>
|
||||
#include <gnutls_sig.h>
|
||||
#include <stdio.h>
|
||||
#include <gcrypt.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define OPENPGP_NAME_SIZE 256
|
||||
|
||||
#define datum_append(x, y, z) mhd_gtls_datum_append_m (x, y, z, gnutls_realloc)
|
||||
|
||||
static void
|
||||
release_mpi_array (mpi_t * arr, size_t n)
|
||||
{
|
||||
mpi_t x;
|
||||
|
||||
while (arr && n--)
|
||||
{
|
||||
x = *arr;
|
||||
mhd_gtls_mpi_release (&x);
|
||||
*arr = NULL;
|
||||
arr++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Map an OpenCDK error type to a GnuTLS error type. */
|
||||
int
|
||||
_gnutls_map_cdk_rc (int rc)
|
||||
{
|
||||
switch (rc)
|
||||
{
|
||||
case CDK_Success:
|
||||
return 0;
|
||||
case CDK_Too_Short:
|
||||
return GNUTLS_E_SHORT_MEMORY_BUFFER;
|
||||
case CDK_General_Error:
|
||||
return GNUTLS_E_INTERNAL_ERROR;
|
||||
case CDK_File_Error:
|
||||
return GNUTLS_E_FILE_ERROR;
|
||||
case CDK_MPI_Error:
|
||||
return GNUTLS_E_MPI_SCAN_FAILED;
|
||||
case CDK_Error_No_Key:
|
||||
return GNUTLS_E_OPENPGP_GETKEY_FAILED;
|
||||
case CDK_Armor_Error:
|
||||
return GNUTLS_E_BASE64_DECODING_ERROR;
|
||||
case CDK_Inv_Value:
|
||||
return GNUTLS_E_INVALID_REQUEST;
|
||||
default:
|
||||
return GNUTLS_E_INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
buftou32 (const uint8_t * buf)
|
||||
{
|
||||
unsigned a;
|
||||
a = buf[0] << 24;
|
||||
a |= buf[1] << 16;
|
||||
a |= buf[2] << 8;
|
||||
a |= buf[3];
|
||||
return a;
|
||||
}
|
||||
|
||||
static int
|
||||
openpgp_pk_to_gnutls_cert (gnutls_cert * cert, cdk_pkt_pubkey_t pk)
|
||||
{
|
||||
uint8_t buf[512 + 2];
|
||||
size_t nbytes;
|
||||
int algo, i;
|
||||
int rc = 0;
|
||||
|
||||
if (!cert || !pk)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_INVALID_REQUEST;
|
||||
}
|
||||
|
||||
/* GnuTLS OpenPGP does not support ELG keys */
|
||||
if (is_ELG (pk->pubkey_algo))
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_UNWANTED_ALGORITHM;
|
||||
}
|
||||
|
||||
algo = MHD_GNUTLS_PK_RSA;
|
||||
cert->subject_pk_algorithm = algo;
|
||||
cert->version = pk->version;
|
||||
cert->cert_type = MHD_GNUTLS_CRT_OPENPGP;
|
||||
|
||||
cert->key_usage = 0;
|
||||
if (pk->pubkey_usage & CDK_KEY_USG_SIGN)
|
||||
cert->key_usage = KEY_DIGITAL_SIGNATURE;
|
||||
if (pk->pubkey_usage & CDK_KEY_USG_ENCR)
|
||||
cert->key_usage = KEY_KEY_ENCIPHERMENT;
|
||||
if (!cert->key_usage) /* Fallback code. */
|
||||
{
|
||||
if (pk->pubkey_algo == GCRY_PK_DSA || pk->pubkey_algo == GCRY_PK_RSA_S)
|
||||
cert->key_usage = KEY_DIGITAL_SIGNATURE;
|
||||
else if (pk->pubkey_algo == GCRY_PK_RSA_E)
|
||||
cert->key_usage = KEY_KEY_ENCIPHERMENT;
|
||||
else if (pk->pubkey_algo == GCRY_PK_RSA)
|
||||
cert->key_usage = KEY_DIGITAL_SIGNATURE | KEY_KEY_ENCIPHERMENT;
|
||||
}
|
||||
|
||||
cert->params_size = cdk_pk_get_npkey (pk->pubkey_algo);
|
||||
for (i = 0; i < cert->params_size; i++)
|
||||
{
|
||||
nbytes = sizeof (buf) / sizeof (buf[0]);
|
||||
cdk_pk_get_mpi (pk, i, buf, nbytes, &nbytes, NULL);
|
||||
rc = mhd_gtls_mpi_scan_pgp (&cert->params[i], buf, &nbytes);
|
||||
if (rc)
|
||||
{
|
||||
rc = GNUTLS_E_MPI_SCAN_FAILED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc)
|
||||
release_mpi_array (cert->params, i - 1);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*-
|
||||
* _gnutls_openpgp_raw_privkey_to_gkey - Converts an OpenPGP secret key to GnuTLS
|
||||
* @pkey: the GnuTLS private key context to store the key.
|
||||
* @raw_key: the raw data which contains the whole key packets.
|
||||
* @format: the format of the key packets.
|
||||
*
|
||||
* The RFC2440 (OpenPGP Message Format) data is converted into the
|
||||
* GnuTLS specific data which is need to perform secret key operations.
|
||||
*
|
||||
* This function can read both BASE64 and RAW keys.
|
||||
-*/
|
||||
int
|
||||
_gnutls_openpgp_raw_privkey_to_gkey (gnutls_privkey * pkey,
|
||||
const gnutls_datum_t * raw_key,
|
||||
gnutls_openpgp_crt_fmt_t format)
|
||||
{
|
||||
cdk_kbnode_t snode = NULL;
|
||||
cdk_packet_t pkt;
|
||||
cdk_stream_t out;
|
||||
cdk_pkt_seckey_t sk = NULL;
|
||||
int pke_algo, i, j;
|
||||
size_t nbytes = 0;
|
||||
uint8_t buf[512];
|
||||
int rc = 0;
|
||||
|
||||
if (!pkey || raw_key->size <= 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
}
|
||||
|
||||
rc = cdk_stream_tmp_new (&out);
|
||||
if (rc)
|
||||
return GNUTLS_E_CERTIFICATE_ERROR;
|
||||
|
||||
if (format == GNUTLS_OPENPGP_FMT_BASE64)
|
||||
{
|
||||
rc = cdk_stream_set_armor_flag (out, 0);
|
||||
if (rc)
|
||||
{
|
||||
cdk_stream_close (out);
|
||||
rc = _gnutls_map_cdk_rc (rc);
|
||||
gnutls_assert ();
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
cdk_stream_write (out, raw_key->data, raw_key->size);
|
||||
cdk_stream_seek (out, 0);
|
||||
|
||||
rc = cdk_keydb_get_keyblock (out, &snode);
|
||||
cdk_stream_close (out);
|
||||
if (rc)
|
||||
{
|
||||
rc = GNUTLS_E_OPENPGP_GETKEY_FAILED;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
pkt = cdk_kbnode_find_packet (snode, CDK_PKT_SECRET_KEY);
|
||||
if (!pkt)
|
||||
{
|
||||
rc = GNUTLS_E_OPENPGP_GETKEY_FAILED;
|
||||
goto leave;
|
||||
}
|
||||
sk = pkt->pkt.secret_key;
|
||||
pke_algo = sk->pk->pubkey_algo;
|
||||
pkey->params_size = cdk_pk_get_npkey (pke_algo);
|
||||
for (i = 0; i < pkey->params_size; i++)
|
||||
{
|
||||
nbytes = sizeof (buf) / sizeof (buf[0]);
|
||||
cdk_pk_get_mpi (sk->pk, i, buf, nbytes, &nbytes, NULL);
|
||||
rc = mhd_gtls_mpi_scan_pgp (&pkey->params[i], buf, &nbytes);
|
||||
if (rc)
|
||||
{
|
||||
rc = GNUTLS_E_MPI_SCAN_FAILED;
|
||||
release_mpi_array (pkey->params, i - 1);
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
pkey->params_size += cdk_pk_get_nskey (pke_algo);
|
||||
for (j = 0; j < cdk_pk_get_nskey (pke_algo); j++, i++)
|
||||
{
|
||||
nbytes = sizeof (buf) / sizeof (buf[0]);
|
||||
cdk_sk_get_mpi (sk, j, buf, nbytes, &nbytes, NULL);
|
||||
rc = mhd_gtls_mpi_scan_pgp (&pkey->params[i], buf, &nbytes);
|
||||
if (rc)
|
||||
{
|
||||
rc = GNUTLS_E_MPI_SCAN_FAILED;
|
||||
release_mpi_array (pkey->params, i - 1);
|
||||
goto leave;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_ELG (pke_algo))
|
||||
return GNUTLS_E_UNWANTED_ALGORITHM;
|
||||
else if (is_RSA (pke_algo))
|
||||
pkey->pk_algorithm = MHD_GNUTLS_PK_RSA;
|
||||
|
||||
leave:
|
||||
cdk_kbnode_release (snode);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*-
|
||||
* _gnutls_openpgp_raw_key_to_gcert - Converts raw OpenPGP data to GnuTLS certs
|
||||
* @cert: the certificate to store the data.
|
||||
* @raw: the buffer which contains the whole OpenPGP key packets.
|
||||
*
|
||||
* The RFC2440 (OpenPGP Message Format) data is converted to a GnuTLS
|
||||
* specific certificate.
|
||||
-*/
|
||||
int
|
||||
_gnutls_openpgp_raw_key_to_gcert (gnutls_cert * cert,
|
||||
const gnutls_datum_t * raw)
|
||||
{
|
||||
cdk_kbnode_t knode = NULL;
|
||||
cdk_packet_t pkt = NULL;
|
||||
int rc;
|
||||
|
||||
if (!cert)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_INVALID_REQUEST;
|
||||
}
|
||||
|
||||
memset (cert, 0, sizeof *cert);
|
||||
|
||||
rc = cdk_kbnode_read_from_mem (&knode, raw->data, raw->size);
|
||||
if (!(rc = _gnutls_map_cdk_rc (rc)))
|
||||
{
|
||||
pkt = cdk_kbnode_find_packet (knode, CDK_PKT_PUBLIC_KEY);
|
||||
}
|
||||
if (!pkt)
|
||||
{
|
||||
gnutls_assert ();
|
||||
rc = _gnutls_map_cdk_rc (rc);
|
||||
}
|
||||
if (!rc)
|
||||
rc = _gnutls_set_datum (&cert->raw, raw->data, raw->size);
|
||||
if (!rc)
|
||||
rc = openpgp_pk_to_gnutls_cert (cert, pkt->pkt.public_key);
|
||||
|
||||
cdk_kbnode_release (knode);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_certificate_set_openpgp_key - Used to set keys in a mhd_gtls_cert_credentials_t structure
|
||||
* @res: is an #mhd_gtls_cert_credentials_t structure.
|
||||
* @key: contains an openpgp public key
|
||||
* @pkey: is an openpgp private key
|
||||
*
|
||||
* This function sets a certificate/private key pair in the
|
||||
* mhd_gtls_cert_credentials_t structure. This function may be called
|
||||
* more than once (in case multiple keys/certificates exist for the
|
||||
* server).
|
||||
*
|
||||
**/
|
||||
int
|
||||
gnutls_certificate_set_openpgp_key (mhd_gtls_cert_credentials_t
|
||||
res, gnutls_openpgp_crt_t crt,
|
||||
gnutls_openpgp_privkey_t pkey)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* this should be first */
|
||||
|
||||
res->pkey = mhd_gtls_realloc_fast (res->pkey,
|
||||
(res->ncerts + 1) *
|
||||
sizeof (gnutls_privkey));
|
||||
if (res->pkey == NULL)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
ret = _gnutls_openpgp_privkey_to_gkey (&res->pkey[res->ncerts], pkey);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
res->cert_list = mhd_gtls_realloc_fast (res->cert_list,
|
||||
(1 +
|
||||
res->ncerts) *
|
||||
sizeof (gnutls_cert *));
|
||||
if (res->cert_list == NULL)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
res->cert_list_length = mhd_gtls_realloc_fast (res->cert_list_length,
|
||||
(1 +
|
||||
res->ncerts) * sizeof (int));
|
||||
if (res->cert_list_length == NULL)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
res->cert_list[res->ncerts] = gnutls_calloc (1, sizeof (gnutls_cert));
|
||||
if (res->cert_list[res->ncerts] == NULL)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
res->cert_list_length[res->ncerts] = 1;
|
||||
|
||||
ret = _gnutls_openpgp_crt_to_gcert (res->cert_list[res->ncerts], crt);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
res->ncerts++;
|
||||
|
||||
/* FIXME: Check if the keys match. */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*-
|
||||
* gnutls_openpgp_get_key - Retrieve a key from the keyring.
|
||||
* @key: the destination context to save the key.
|
||||
* @keyring: the datum struct that contains all keyring information.
|
||||
* @attr: The attribute (keyid, fingerprint, ...).
|
||||
* @by: What attribute is used.
|
||||
*
|
||||
* This function can be used to retrieve keys by different pattern
|
||||
* from a binary or a file keyring.
|
||||
-*/
|
||||
int
|
||||
gnutls_openpgp_get_key (gnutls_datum_t * key,
|
||||
gnutls_openpgp_keyring_t keyring, key_attr_t by,
|
||||
opaque * pattern)
|
||||
{
|
||||
cdk_kbnode_t knode = NULL;
|
||||
unsigned long keyid[2];
|
||||
unsigned char *buf;
|
||||
void *desc;
|
||||
size_t len;
|
||||
int rc = 0;
|
||||
|
||||
if (!key || !keyring || by == KEY_ATTR_NONE)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_INVALID_REQUEST;
|
||||
}
|
||||
|
||||
memset (key, 0, sizeof *key);
|
||||
|
||||
if (by == KEY_ATTR_SHORT_KEYID)
|
||||
{
|
||||
keyid[0] = buftou32 (pattern);
|
||||
desc = keyid;
|
||||
}
|
||||
else if (by == KEY_ATTR_KEYID)
|
||||
{
|
||||
keyid[0] = buftou32 (pattern);
|
||||
keyid[1] = buftou32 (pattern + 4);
|
||||
desc = keyid;
|
||||
}
|
||||
else
|
||||
desc = pattern;
|
||||
rc = cdk_keydb_search_start (keyring->db, by, desc);
|
||||
if (!rc)
|
||||
rc = cdk_keydb_search (keyring->db, &knode);
|
||||
if (rc)
|
||||
{
|
||||
rc = _gnutls_map_cdk_rc (rc);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
if (!cdk_kbnode_find (knode, CDK_PKT_PUBLIC_KEY))
|
||||
{
|
||||
rc = GNUTLS_E_OPENPGP_GETKEY_FAILED;
|
||||
goto leave;
|
||||
}
|
||||
|
||||
/* We let the function allocate the buffer to avoid
|
||||
to call the function twice. */
|
||||
rc = cdk_kbnode_write_to_mem_alloc (knode, &buf, &len);
|
||||
if (!rc)
|
||||
datum_append (key, buf, len);
|
||||
cdk_free (buf);
|
||||
|
||||
leave:
|
||||
cdk_kbnode_release (knode);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/* Convert the stream to a datum. In this case we use the mmap
|
||||
function to map the entire stream to a buffer. */
|
||||
static int
|
||||
stream_to_datum (cdk_stream_t inp, gnutls_datum_t * raw)
|
||||
{
|
||||
uint8_t *buf;
|
||||
size_t buflen;
|
||||
|
||||
if (!inp || !raw)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_INVALID_REQUEST;
|
||||
}
|
||||
|
||||
cdk_stream_mmap (inp, &buf, &buflen);
|
||||
datum_append (raw, buf, buflen);
|
||||
cdk_free (buf);
|
||||
|
||||
if (!buflen)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* gnutls_certificate_set_openpgp_key_mem - Used to set OpenPGP keys
|
||||
* @res: the destination context to save the data.
|
||||
* @cert: the datum that contains the public key.
|
||||
* @key: the datum that contains the secret key.
|
||||
*
|
||||
* This funtion is used to load OpenPGP keys into the GnuTLS credential
|
||||
* structure.
|
||||
* It doesn't matter whether the keys are armored or not, but the files
|
||||
* should only contain one key which should not be encrypted.
|
||||
**/
|
||||
int
|
||||
gnutls_certificate_set_openpgp_key_mem (mhd_gtls_cert_credentials_t
|
||||
res, const gnutls_datum_t * icert,
|
||||
const gnutls_datum_t * ikey,
|
||||
gnutls_openpgp_crt_fmt_t format)
|
||||
{
|
||||
gnutls_openpgp_privkey_t key;
|
||||
gnutls_openpgp_crt_t cert;
|
||||
int ret;
|
||||
|
||||
ret = gnutls_openpgp_privkey_init (&key);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gnutls_openpgp_privkey_import (key, ikey, format, NULL, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
gnutls_openpgp_privkey_deinit (key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gnutls_openpgp_crt_init (&cert);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
gnutls_openpgp_privkey_deinit (key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gnutls_openpgp_crt_import (cert, icert, format);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
gnutls_openpgp_privkey_deinit (key);
|
||||
gnutls_openpgp_crt_deinit (cert);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
ret = gnutls_certificate_set_openpgp_key (res, cert, key);
|
||||
|
||||
gnutls_openpgp_privkey_deinit (key);
|
||||
gnutls_openpgp_crt_deinit (cert);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gnutls_certificate_set_openpgp_key_file - Used to set OpenPGP keys
|
||||
* @res: the destination context to save the data.
|
||||
* @certfile: the file that contains the public key.
|
||||
* @keyfile: the file that contains the secret key.
|
||||
*
|
||||
* This funtion is used to load OpenPGP keys into the GnuTLS credentials structure.
|
||||
* It doesn't matter whether the keys are armored or not, but the files
|
||||
* should only contain one key which should not be encrypted.
|
||||
**/
|
||||
int
|
||||
gnutls_certificate_set_openpgp_key_file (mhd_gtls_cert_credentials_t
|
||||
res, const char *certfile,
|
||||
const char *keyfile,
|
||||
gnutls_openpgp_crt_fmt_t format)
|
||||
{
|
||||
struct stat statbuf;
|
||||
gnutls_datum_t key, cert;
|
||||
int rc;
|
||||
size_t size;
|
||||
|
||||
if (!res || !keyfile || !certfile)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_INVALID_REQUEST;
|
||||
}
|
||||
|
||||
if (stat (certfile, &statbuf) || stat (keyfile, &statbuf))
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_FILE_ERROR;
|
||||
}
|
||||
|
||||
cert.data = read_binary_file (certfile, &size);
|
||||
cert.size = (unsigned int) size;
|
||||
if (cert.data == NULL)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_FILE_ERROR;
|
||||
}
|
||||
|
||||
key.data = read_binary_file (keyfile, &size);
|
||||
key.size = (unsigned int) size;
|
||||
if (key.data == NULL)
|
||||
{
|
||||
gnutls_assert ();
|
||||
free (cert.data);
|
||||
return GNUTLS_E_FILE_ERROR;
|
||||
}
|
||||
|
||||
rc = gnutls_certificate_set_openpgp_key_mem (res, &cert, &key, format);
|
||||
|
||||
free (cert.data);
|
||||
free (key.data);
|
||||
|
||||
if (rc < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
gnutls_openpgp_count_key_names (const gnutls_datum_t * cert)
|
||||
{
|
||||
cdk_kbnode_t knode, p, ctx;
|
||||
cdk_packet_t pkt;
|
||||
int nuids;
|
||||
|
||||
if (cert == NULL)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cdk_kbnode_read_from_mem (&knode, cert->data, cert->size))
|
||||
{
|
||||
gnutls_assert ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx = NULL;
|
||||
for (nuids = 0;;)
|
||||
{
|
||||
p = cdk_kbnode_walk (knode, &ctx, 0);
|
||||
if (!p)
|
||||
break;
|
||||
pkt = cdk_kbnode_get_packet (p);
|
||||
if (pkt->pkttype == CDK_PKT_USER_ID)
|
||||
nuids++;
|
||||
}
|
||||
|
||||
cdk_kbnode_release (knode);
|
||||
return nuids;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gnutls_certificate_set_openpgp_keyring_file - Sets a keyring file for OpenPGP
|
||||
* @c: A certificate credentials structure
|
||||
* @file: filename of the keyring.
|
||||
*
|
||||
* The function is used to set keyrings that will be used internally
|
||||
* by various OpenPGP functions. For example to find a key when it
|
||||
* is needed for an operations. The keyring will also be used at the
|
||||
* verification functions.
|
||||
*
|
||||
**/
|
||||
int
|
||||
gnutls_certificate_set_openpgp_keyring_file (mhd_gtls_cert_credentials_t
|
||||
c, const char *file,
|
||||
gnutls_openpgp_crt_fmt_t format)
|
||||
{
|
||||
gnutls_datum_t ring;
|
||||
size_t size;
|
||||
int rc;
|
||||
|
||||
if (!c || !file)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_INVALID_REQUEST;
|
||||
}
|
||||
|
||||
ring.data = read_binary_file (file, &size);
|
||||
ring.size = (unsigned int) size;
|
||||
if (ring.data == NULL)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_FILE_ERROR;
|
||||
}
|
||||
|
||||
rc =
|
||||
gnutls_certificate_set_openpgp_keyring_mem (c, ring.data, ring.size,
|
||||
format);
|
||||
|
||||
free (ring.data);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_certificate_set_openpgp_keyring_mem - Add keyring data for OpenPGP
|
||||
* @c: A certificate credentials structure
|
||||
* @data: buffer with keyring data.
|
||||
* @dlen: length of data buffer.
|
||||
*
|
||||
* The function is used to set keyrings that will be used internally
|
||||
* by various OpenPGP functions. For example to find a key when it
|
||||
* is needed for an operations. The keyring will also be used at the
|
||||
* verification functions.
|
||||
*
|
||||
**/
|
||||
int
|
||||
gnutls_certificate_set_openpgp_keyring_mem (mhd_gtls_cert_credentials_t
|
||||
c, const opaque * data,
|
||||
size_t dlen,
|
||||
gnutls_openpgp_crt_fmt_t format)
|
||||
{
|
||||
#ifndef KEYRING_HACK
|
||||
cdk_stream_t inp;
|
||||
size_t count;
|
||||
uint8_t *buf;
|
||||
gnutls_datum ddata;
|
||||
int rc;
|
||||
|
||||
ddata.data = (void *) data;
|
||||
ddata.size = dlen;
|
||||
|
||||
if (!c || !data || !dlen)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_INVALID_REQUEST;
|
||||
}
|
||||
|
||||
rc = gnutls_openpgp_keyring_init (&c->keyring);
|
||||
if (rc < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = gnutls_openpgp_keyring_import (c->keyring, &ddata, format);
|
||||
if (rc < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
gnutls_openpgp_keyring_deinit (c->keyring);
|
||||
return rc;
|
||||
}
|
||||
#else
|
||||
c->keyring_format = format;
|
||||
c->keyring.data = gnutls_malloc (dlen + 1);
|
||||
if (c->keyring.data == NULL)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_MEMORY_ERROR;
|
||||
}
|
||||
memcpy (c->keyring.data, data, dlen);
|
||||
c->keyring.data[dlen] = 0;
|
||||
c->keyring.size = dlen;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*-
|
||||
* _gnutls_openpgp_request_key - Receives a key from a database, key server etc
|
||||
* @ret - a pointer to gnutls_datum_t structure.
|
||||
* @cred - a mhd_gtls_cert_credentials_t structure.
|
||||
* @key_fingerprint - The keyFingerprint
|
||||
* @key_fingerprint_size - the size of the fingerprint
|
||||
*
|
||||
* Retrieves a key from a local database, keyring, or a key server. The
|
||||
* return value is locally allocated.
|
||||
*
|
||||
-*/
|
||||
int
|
||||
_gnutls_openpgp_request_key (mhd_gtls_session_t session, gnutls_datum_t * ret,
|
||||
const mhd_gtls_cert_credentials_t cred,
|
||||
opaque * key_fpr, int key_fpr_size)
|
||||
{
|
||||
int rc = 0;
|
||||
#ifdef KEYRING_HACK
|
||||
gnutls_openpgp_keyring_t kring;
|
||||
#endif
|
||||
|
||||
if (!ret || !cred || !key_fpr)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_INVALID_REQUEST;
|
||||
}
|
||||
|
||||
if (key_fpr_size != 16 && key_fpr_size != 20)
|
||||
return GNUTLS_E_HASH_FAILED; /* only MD5 and SHA1 are supported */
|
||||
|
||||
#ifndef KEYRING_HACK
|
||||
rc = gnutls_openpgp_get_key (ret, cred->keyring, KEY_ATTR_FPR, key_fpr);
|
||||
#else
|
||||
rc = gnutls_openpgp_keyring_init (&kring);
|
||||
if (rc < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc =
|
||||
gnutls_openpgp_keyring_import (kring, &cred->keyring,
|
||||
cred->keyring_format);
|
||||
if (rc < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
gnutls_openpgp_keyring_deinit (kring);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
if (rc >= 0) /* key was found */
|
||||
{
|
||||
rc = 0;
|
||||
goto error;
|
||||
}
|
||||
else
|
||||
rc = GNUTLS_E_OPENPGP_GETKEY_FAILED;
|
||||
|
||||
/* If the callback function was set, then try this one. */
|
||||
if (session->internals.openpgp_recv_key_func != NULL)
|
||||
{
|
||||
rc = session->internals.openpgp_recv_key_func (session,
|
||||
key_fpr,
|
||||
key_fpr_size, ret);
|
||||
if (rc < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
rc = GNUTLS_E_OPENPGP_GETKEY_FAILED;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
#ifdef KEYRING_HACK
|
||||
gnutls_openpgp_keyring_deinit (kring);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_set_recv_key_function - Used to set a key retrieval callback for PGP keys
|
||||
* @session: a TLS session
|
||||
* @func: the callback
|
||||
*
|
||||
* This funtion will set a key retrieval function for OpenPGP keys. This
|
||||
* callback is only useful in server side, and will be used if the peer
|
||||
* sent a key fingerprint instead of a full key.
|
||||
*
|
||||
**/
|
||||
void
|
||||
gnutls_openpgp_set_recv_key_function (mhd_gtls_session_t session,
|
||||
mhd_gtls_openpgp_recv_key_func func)
|
||||
{
|
||||
session->internals.openpgp_recv_key_func = func;
|
||||
}
|
||||
|
||||
|
||||
/* Copies a gnutls_openpgp_privkey_t to a gnutls_privkey structure. */
|
||||
int
|
||||
_gnutls_openpgp_privkey_to_gkey (gnutls_privkey * dest,
|
||||
gnutls_openpgp_privkey_t src)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
memset (dest, 0, sizeof (gnutls_privkey));
|
||||
|
||||
for (i = 0; i < src->pkey.params_size; i++)
|
||||
{
|
||||
dest->params[i] = _gnutls_mpi_copy (src->pkey.params[i]);
|
||||
if (dest->params[i] == NULL)
|
||||
{
|
||||
gnutls_assert ();
|
||||
ret = GNUTLS_E_MEMORY_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
dest->pk_algorithm = src->pkey.pk_algorithm;
|
||||
dest->params_size = src->pkey.params_size;
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
for (i = 0; i < src->pkey.params_size; i++)
|
||||
mhd_gtls_mpi_release (&dest->params[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Converts a parsed gnutls_openpgp_crt_t to a gnutls_cert structure.
|
||||
*/
|
||||
int
|
||||
_gnutls_openpgp_crt_to_gcert (gnutls_cert * gcert, gnutls_openpgp_crt_t cert)
|
||||
{
|
||||
opaque *der;
|
||||
size_t der_size = 0;
|
||||
gnutls_datum_t raw;
|
||||
int ret;
|
||||
|
||||
memset (gcert, 0, sizeof (gnutls_cert));
|
||||
gcert->cert_type = MHD_GNUTLS_CRT_OPENPGP;
|
||||
|
||||
|
||||
ret = gnutls_openpgp_crt_export (cert, GNUTLS_OPENPGP_FMT_RAW,
|
||||
NULL, &der_size);
|
||||
if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
der = gnutls_malloc (der_size);
|
||||
if (der == NULL)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
ret = gnutls_openpgp_crt_export (cert, GNUTLS_OPENPGP_FMT_RAW,
|
||||
der, &der_size);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
gnutls_free (der);
|
||||
return ret;
|
||||
}
|
||||
|
||||
raw.data = der;
|
||||
raw.size = der_size;
|
||||
|
||||
ret = _gnutls_openpgp_raw_key_to_gcert (gcert, &raw);
|
||||
if (ret < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
gnutls_free (der);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gnutls_free (der);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_privkey_sign_hash - This function will sign the given data using the private key params
|
||||
* @key: Holds the key
|
||||
* @hash: holds the data to be signed
|
||||
* @signature: will contain newly allocated signature
|
||||
*
|
||||
* This function will sign the given hash using the private key.
|
||||
*
|
||||
* Return value: In case of failure a negative value will be returned,
|
||||
* and 0 on success.
|
||||
**/
|
||||
int
|
||||
gnutls_openpgp_privkey_sign_hash (gnutls_openpgp_privkey_t key,
|
||||
const gnutls_datum_t * hash,
|
||||
gnutls_datum_t * signature)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (key == NULL)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_INVALID_REQUEST;
|
||||
}
|
||||
|
||||
result = mhd_gtls_sign (key->pkey.pk_algorithm, key->pkey.params,
|
||||
key->pkey.params_size, hash, signature);
|
||||
if (result < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
#include <config.h>
|
||||
|
||||
#if ENABLE_OPENPGP
|
||||
|
||||
#ifndef GNUTLS_OPENPGP_H
|
||||
#define GNUTLS_OPENPGP_H
|
||||
|
||||
#include <auth_cert.h>
|
||||
#include <opencdk.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int type;
|
||||
size_t size;
|
||||
uint8_t *data;
|
||||
}keybox_blob;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
KBX_BLOB_FILE = 0x00,
|
||||
KBX_BLOB_DATA = 0x01
|
||||
}keyring_blob_types;
|
||||
|
||||
/* OpenCDK compatible */
|
||||
typedef enum
|
||||
{
|
||||
KEY_ATTR_NONE = 0,
|
||||
KEY_ATTR_SHORT_KEYID = 3,
|
||||
KEY_ATTR_KEYID = 4,
|
||||
KEY_ATTR_FPR = 5
|
||||
}key_attr_t;
|
||||
|
||||
int
|
||||
gnutls_certificate_set_openpgp_key_file (mhd_gtls_cert_credentials_t
|
||||
res, const char *CERTFILE,
|
||||
const char *KEYFILE, gnutls_openpgp_crt_fmt_t);
|
||||
|
||||
int gnutls_openpgp_count_key_names (const gnutls_datum_t * cert);
|
||||
|
||||
int gnutls_certificate_set_openpgp_keyring_file
|
||||
(mhd_gtls_cert_credentials_t c, const char *file, gnutls_openpgp_crt_fmt_t);
|
||||
|
||||
int
|
||||
gnutls_certificate_set_openpgp_keyring_mem (mhd_gtls_cert_credentials_t
|
||||
c, const opaque * data,
|
||||
size_t dlen, gnutls_openpgp_crt_fmt_t);
|
||||
|
||||
int gnutls_openpgp_get_key (gnutls_datum_t * key,
|
||||
gnutls_openpgp_keyring_t keyring,
|
||||
key_attr_t by, opaque * pattern);
|
||||
|
||||
int gnutls_openpgp_recv_key (const char *host,
|
||||
short port, uint32_t keyid,
|
||||
gnutls_datum_t * key);
|
||||
|
||||
/* internal */
|
||||
int _gnutls_openpgp_raw_key_to_gcert (gnutls_cert * cert,
|
||||
const gnutls_datum_t * raw);
|
||||
|
||||
extern int
|
||||
_gnutls_openpgp_raw_privkey_to_gkey (gnutls_privkey * pkey,
|
||||
const gnutls_datum_t * raw_key,
|
||||
gnutls_openpgp_crt_fmt_t format);
|
||||
|
||||
int
|
||||
_gnutls_openpgp_request_key (mhd_gtls_session_t,
|
||||
gnutls_datum_t * ret,
|
||||
const mhd_gtls_cert_credentials_t cred,
|
||||
opaque * key_fpr, int key_fpr_size);
|
||||
|
||||
int _gnutls_openpgp_verify_key (const mhd_gtls_cert_credentials_t,
|
||||
const gnutls_datum_t * cert_list,
|
||||
int cert_list_length, unsigned int *status);
|
||||
int _gnutls_openpgp_fingerprint (const gnutls_datum_t * cert,
|
||||
unsigned char *fpr, size_t * fprlen);
|
||||
time_t _gnutls_openpgp_get_raw_key_creation_time (const gnutls_datum_t *
|
||||
cert);
|
||||
time_t _gnutls_openpgp_get_raw_key_expiration_time (const gnutls_datum_t *
|
||||
cert);
|
||||
|
||||
int
|
||||
gnutls_openpgp_privkey_init (gnutls_openpgp_privkey_t * key);
|
||||
|
||||
int
|
||||
gnutls_openpgp_privkey_init (gnutls_openpgp_privkey_t * key);
|
||||
|
||||
void
|
||||
gnutls_openpgp_privkey_deinit (gnutls_openpgp_privkey_t key);
|
||||
|
||||
int
|
||||
gnutls_openpgp_privkey_import (gnutls_openpgp_privkey_t key,
|
||||
const gnutls_datum_t * data,
|
||||
gnutls_openpgp_crt_fmt_t format,
|
||||
const char *pass, unsigned int flags);
|
||||
|
||||
#endif /*GNUTLS_OPENPGP_H */
|
||||
|
||||
#endif /*ENABLE_OPENPGP */
|
||||
@@ -1,182 +0,0 @@
|
||||
#ifndef OPENPGP_H
|
||||
#define OPENPGP_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if ENABLE_OPENPGP
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <gnutls.h>
|
||||
#include "opencdk.h"
|
||||
#include <gnutls_cert.h>
|
||||
|
||||
/* Internal context to store the OpenPGP key. */
|
||||
typedef struct gnutls_openpgp_crt_int
|
||||
{
|
||||
cdk_kbnode_t knode;
|
||||
} gnutls_openpgp_crt_int;
|
||||
|
||||
/* Internal context to store the private OpenPGP key. */
|
||||
typedef struct gnutls_openpgp_privkey_int
|
||||
{
|
||||
gnutls_privkey pkey;
|
||||
} gnutls_openpgp_privkey_int;
|
||||
|
||||
typedef struct gnutls_openpgp_keyring_int
|
||||
{
|
||||
cdk_keydb_hd_t db;
|
||||
cdk_stream_t db_stream;
|
||||
} gnutls_openpgp_keyring_int;
|
||||
|
||||
typedef struct gnutls_openpgp_keyring_int * gnutls_openpgp_keyring_t;
|
||||
/* gnutls_openpgp_cert_t should be defined in gnutls.h */
|
||||
|
||||
/* initializes the memory for gnutls_openpgp_crt_t struct */
|
||||
int gnutls_openpgp_crt_init(gnutls_openpgp_crt_t * key);
|
||||
/* frees all memory */
|
||||
void gnutls_openpgp_crt_deinit(gnutls_openpgp_crt_t key);
|
||||
|
||||
int gnutls_openpgp_crt_import(gnutls_openpgp_crt_t key,
|
||||
const gnutls_datum_t * data,
|
||||
gnutls_openpgp_crt_fmt_t format);
|
||||
int gnutls_openpgp_crt_export(gnutls_openpgp_crt_t key,
|
||||
gnutls_openpgp_crt_fmt_t format,
|
||||
void *output_data,
|
||||
size_t * output_data_size);
|
||||
|
||||
/* The key_usage flags are defined in gnutls.h. They are
|
||||
* the GNUTLS_KEY_* definitions.
|
||||
*/
|
||||
int gnutls_openpgp_crt_get_key_usage(gnutls_openpgp_crt_t cert,
|
||||
unsigned int *key_usage);
|
||||
int gnutls_openpgp_crt_get_fingerprint(gnutls_openpgp_crt_t key,
|
||||
void *fpr,
|
||||
size_t * fprlen);
|
||||
|
||||
int gnutls_openpgp_crt_get_name(gnutls_openpgp_crt_t key,
|
||||
int idx,
|
||||
char *buf,
|
||||
size_t * sizeof_buf);
|
||||
|
||||
gnutls_pk_algorithm_t
|
||||
gnutls_openpgp_crt_get_pk_algorithm(gnutls_openpgp_crt_t key,
|
||||
unsigned int *bits);
|
||||
|
||||
int gnutls_openpgp_crt_get_version(gnutls_openpgp_crt_t key);
|
||||
|
||||
time_t gnutls_openpgp_crt_get_creation_time(gnutls_openpgp_crt_t key);
|
||||
time_t gnutls_openpgp_crt_get_expiration_time(gnutls_openpgp_crt_t key);
|
||||
|
||||
int gnutls_openpgp_crt_get_id(gnutls_openpgp_crt_t key,
|
||||
unsigned char keyid[8]);
|
||||
|
||||
int gnutls_openpgp_crt_check_hostname(gnutls_openpgp_crt_t key,
|
||||
const char *hostname);
|
||||
|
||||
/* privkey stuff. */
|
||||
int gnutls_openpgp_privkey_init(gnutls_openpgp_privkey_t * key);
|
||||
void gnutls_openpgp_privkey_deinit(gnutls_openpgp_privkey_t key);
|
||||
gnutls_pk_algorithm_t
|
||||
gnutls_openpgp_privkey_get_pk_algorithm(gnutls_openpgp_privkey_t key,
|
||||
unsigned int *bits);
|
||||
int gnutls_openpgp_privkey_import(gnutls_openpgp_privkey_t key,
|
||||
const gnutls_datum_t * data,
|
||||
gnutls_openpgp_crt_fmt_t format,
|
||||
const char *pass,
|
||||
unsigned int flags);
|
||||
int gnutls_openpgp_privkey_sign_hash(gnutls_openpgp_privkey_t key,
|
||||
const gnutls_datum_t * hash,
|
||||
gnutls_datum_t * signature);
|
||||
|
||||
/* Keyring stuff. */
|
||||
|
||||
int gnutls_openpgp_keyring_init(gnutls_openpgp_keyring_t * keyring);
|
||||
void gnutls_openpgp_keyring_deinit(gnutls_openpgp_keyring_t keyring);
|
||||
|
||||
int gnutls_openpgp_keyring_import(gnutls_openpgp_keyring_t keyring,
|
||||
const gnutls_datum_t * data,
|
||||
gnutls_openpgp_crt_fmt_t format);
|
||||
|
||||
int gnutls_openpgp_keyring_check_id(gnutls_openpgp_keyring_t ring,
|
||||
const unsigned char keyid[8],
|
||||
unsigned int flags);
|
||||
|
||||
int gnutls_openpgp_crt_verify_ring(gnutls_openpgp_crt_t key,
|
||||
gnutls_openpgp_keyring_t keyring,
|
||||
unsigned int flags,
|
||||
unsigned int *verify
|
||||
/* the output of the verification */);
|
||||
|
||||
int gnutls_openpgp_crt_verify_self(gnutls_openpgp_crt_t key,
|
||||
unsigned int flags,
|
||||
unsigned int *verify);
|
||||
|
||||
/* certificate authentication stuff.
|
||||
*/
|
||||
int gnutls_certificate_set_openpgp_key(mhd_gtls_cert_credentials_t
|
||||
res,
|
||||
gnutls_openpgp_crt_t key,
|
||||
gnutls_openpgp_privkey_t pkey);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
int _gnutls_map_cdk_rc(int rc);
|
||||
int gnutls_openpgp_crt_get_name(gnutls_openpgp_crt_t key,
|
||||
int idx,
|
||||
char *buf,
|
||||
size_t * sizeof_buf);
|
||||
int gnutls_openpgp_crt_get_fingerprint(gnutls_openpgp_crt_t key,
|
||||
void *fpr,
|
||||
size_t * fprlen);
|
||||
gnutls_pk_algorithm_t
|
||||
gnutls_openpgp_crt_get_pk_algorithm(gnutls_openpgp_crt_t key,
|
||||
unsigned int *bits);
|
||||
int gnutls_openpgp_crt_get_version(gnutls_openpgp_crt_t key);
|
||||
time_t gnutls_openpgp_crt_get_creation_time(gnutls_openpgp_crt_t key);
|
||||
time_t gnutls_openpgp_crt_get_expiration_time(gnutls_openpgp_crt_t key);
|
||||
int gnutls_openpgp_crt_get_id(gnutls_openpgp_crt_t key,
|
||||
unsigned char keyid[8]);
|
||||
|
||||
int gnutls_openpgp_crt_init(gnutls_openpgp_crt_t * key);
|
||||
void gnutls_openpgp_crt_deinit(gnutls_openpgp_crt_t key);
|
||||
int gnutls_openpgp_crt_import(gnutls_openpgp_crt_t key,
|
||||
const gnutls_datum_t * data,
|
||||
gnutls_openpgp_crt_fmt_t format);
|
||||
int gnutls_openpgp_crt_export(gnutls_openpgp_crt_t key,
|
||||
gnutls_openpgp_crt_fmt_t format,
|
||||
void *output_data,
|
||||
size_t * output_data_size);
|
||||
|
||||
void gnutls_openpgp_keyring_deinit(gnutls_openpgp_keyring_t keyring);
|
||||
int gnutls_openpgp_keyring_init(gnutls_openpgp_keyring_t * keyring);
|
||||
int gnutls_openpgp_keyring_import(gnutls_openpgp_keyring_t keyring,
|
||||
const gnutls_datum_t * data,
|
||||
gnutls_openpgp_crt_fmt_t format);
|
||||
int gnutls_openpgp_keyring_check_id(gnutls_openpgp_keyring_t ring,
|
||||
const unsigned char keyid[8],
|
||||
unsigned int flags);
|
||||
|
||||
int gnutls_openpgp_crt_verify_ring(gnutls_openpgp_crt_t key,
|
||||
gnutls_openpgp_keyring_t keyring,
|
||||
unsigned int flags,
|
||||
unsigned int *verify);
|
||||
|
||||
int gnutls_openpgp_crt_verify_self(gnutls_openpgp_crt_t key,
|
||||
unsigned int flags,
|
||||
unsigned int *verify);
|
||||
|
||||
int _gnutls_openpgp_crt_to_gcert(gnutls_cert * gcert,
|
||||
gnutls_openpgp_crt_t cert);
|
||||
int _gnutls_openpgp_privkey_to_gkey(gnutls_privkey * dest,
|
||||
gnutls_openpgp_privkey_t src);
|
||||
|
||||
void gnutls_openpgp_privkey_deinit(gnutls_openpgp_privkey_t key);
|
||||
|
||||
#endif /* ENABLE_OPENPGP */
|
||||
#endif /* OPENPGP_H */
|
||||
@@ -1,543 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation
|
||||
*
|
||||
* Author: Timo Schulz, Nikos Mavrogiannopoulos
|
||||
*
|
||||
* This file is part of GNUTLS-EXTRA.
|
||||
*
|
||||
* GNUTLS-EXTRA 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.
|
||||
*
|
||||
* GNUTLS-EXTRA is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Functions on OpenPGP key parsing
|
||||
*/
|
||||
|
||||
#include <gnutls_int.h>
|
||||
#include <gnutls_datum.h>
|
||||
#include <gnutls_global.h>
|
||||
#include <gnutls_errors.h>
|
||||
#include "openpgp.h"
|
||||
/* x509 */
|
||||
#include <rfc2818.h>
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_crt_init - This function initializes a gnutls_openpgp_crt_t structure
|
||||
* @key: The structure to be initialized
|
||||
*
|
||||
* This function will initialize an OpenPGP key structure.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*
|
||||
**/
|
||||
int
|
||||
gnutls_openpgp_crt_init (gnutls_openpgp_crt_t * key)
|
||||
{
|
||||
*key = gnutls_calloc (1, sizeof (gnutls_openpgp_crt_int));
|
||||
|
||||
if (*key)
|
||||
return 0; /* success */
|
||||
return GNUTLS_E_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_crt_deinit - This function deinitializes memory used by a gnutls_openpgp_crt_t structure
|
||||
* @key: The structure to be initialized
|
||||
*
|
||||
* This function will deinitialize a key structure.
|
||||
**/
|
||||
void
|
||||
gnutls_openpgp_crt_deinit (gnutls_openpgp_crt_t key)
|
||||
{
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
if (key->knode)
|
||||
{
|
||||
cdk_kbnode_release (key->knode);
|
||||
key->knode = NULL;
|
||||
}
|
||||
|
||||
gnutls_free (key);
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_crt_import - This function will import a RAW or BASE64 encoded key
|
||||
* @key: The structure to store the parsed key.
|
||||
* @data: The RAW or BASE64 encoded key.
|
||||
* @format: One of gnutls_openpgp_crt_fmt_t elements.
|
||||
*
|
||||
* This function will convert the given RAW or Base64 encoded key
|
||||
* to the native gnutls_openpgp_crt_t format. The output will be stored in 'key'.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
**/
|
||||
int
|
||||
gnutls_openpgp_crt_import (gnutls_openpgp_crt_t key,
|
||||
const gnutls_datum_t * data,
|
||||
gnutls_openpgp_crt_fmt_t format)
|
||||
{
|
||||
cdk_stream_t inp;
|
||||
int rc;
|
||||
|
||||
if (format == GNUTLS_OPENPGP_FMT_RAW)
|
||||
rc = cdk_kbnode_read_from_mem (&key->knode, data->data, data->size);
|
||||
else
|
||||
{
|
||||
rc = cdk_stream_tmp_from_mem (data->data, data->size, &inp);
|
||||
if (rc)
|
||||
{
|
||||
rc = _gnutls_map_cdk_rc (rc);
|
||||
gnutls_assert ();
|
||||
return rc;
|
||||
}
|
||||
if (cdk_armor_filter_use (inp))
|
||||
rc = cdk_stream_set_armor_flag (inp, 0);
|
||||
if (!rc)
|
||||
rc = cdk_keydb_get_keyblock (inp, &key->knode);
|
||||
cdk_stream_close (inp);
|
||||
if (rc)
|
||||
{
|
||||
rc = _gnutls_map_cdk_rc (rc);
|
||||
gnutls_assert ();
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_crt_export - This function will export a RAW or BASE64 encoded key
|
||||
* @key: Holds the key.
|
||||
* @format: One of gnutls_openpgp_crt_fmt_t elements.
|
||||
* @output_data: will contain the key base64 encoded or raw
|
||||
* @output_data_size: holds the size of output_data (and will be replaced by the actual size of parameters)
|
||||
*
|
||||
* This function will convert the given key to RAW or Base64 format.
|
||||
* If the buffer provided is not long enough to hold the output, then
|
||||
* GNUTLS_E_SHORT_MEMORY_BUFFER will be returned.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*
|
||||
**/
|
||||
int
|
||||
gnutls_openpgp_crt_export (gnutls_openpgp_crt_t key,
|
||||
gnutls_openpgp_crt_fmt_t format,
|
||||
void *output_data, size_t * output_data_size)
|
||||
{
|
||||
size_t input_data_size = *output_data_size;
|
||||
size_t calc_size;
|
||||
int rc;
|
||||
|
||||
rc = cdk_kbnode_write_to_mem (key->knode, output_data, output_data_size);
|
||||
if (rc)
|
||||
{
|
||||
rc = _gnutls_map_cdk_rc (rc);
|
||||
gnutls_assert ();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* FIXME: The first call of this function is with output_data == NULL
|
||||
to figure out the size and the caller expects this error here. */
|
||||
if (!output_data)
|
||||
return GNUTLS_E_SHORT_MEMORY_BUFFER;
|
||||
|
||||
if (format == GNUTLS_OPENPGP_FMT_BASE64)
|
||||
{
|
||||
unsigned char *in = cdk_calloc (1, *output_data_size);
|
||||
memcpy (in, output_data, *output_data_size);
|
||||
|
||||
/* Calculate the size of the encoded data and check if the provided
|
||||
buffer is large enough. */
|
||||
rc = cdk_armor_encode_buffer (in, input_data_size,
|
||||
NULL, 0, &calc_size, CDK_ARMOR_PUBKEY);
|
||||
if (rc || calc_size > input_data_size)
|
||||
{
|
||||
cdk_free (in);
|
||||
*output_data_size = calc_size;
|
||||
rc = _gnutls_map_cdk_rc (CDK_Too_Short);
|
||||
gnutls_assert ();
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = cdk_armor_encode_buffer (in, input_data_size, output_data,
|
||||
input_data_size, &calc_size,
|
||||
CDK_ARMOR_PUBKEY);
|
||||
cdk_free (in);
|
||||
*output_data_size = calc_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_crt_get_fingerprint - Gets the fingerprint
|
||||
* @key: the raw data that contains the OpenPGP public key.
|
||||
* @fpr: the buffer to save the fingerprint, must hold at least 20 bytes.
|
||||
* @fprlen: the integer to save the length of the fingerprint.
|
||||
*
|
||||
* Returns the fingerprint of the OpenPGP key. Depends on the algorithm,
|
||||
* the fingerprint can be 16 or 20 bytes.
|
||||
**/
|
||||
int
|
||||
gnutls_openpgp_crt_get_fingerprint (gnutls_openpgp_crt_t key,
|
||||
void *fpr, size_t * fprlen)
|
||||
{
|
||||
cdk_packet_t pkt;
|
||||
cdk_pkt_pubkey_t pk = NULL;
|
||||
|
||||
if (!fpr || !fprlen)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_INVALID_REQUEST;
|
||||
}
|
||||
|
||||
*fprlen = 0;
|
||||
|
||||
pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_PUBLIC_KEY);
|
||||
if (!pkt)
|
||||
return GNUTLS_E_OPENPGP_GETKEY_FAILED;
|
||||
|
||||
pk = pkt->pkt.public_key;
|
||||
*fprlen = 20;
|
||||
|
||||
/* FIXME: Check if the draft allows old PGP keys. */
|
||||
if (is_RSA (pk->pubkey_algo) && pk->version < 4)
|
||||
*fprlen = 16;
|
||||
cdk_pk_get_fingerprint (pk, fpr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_gnutls_openpgp_count_key_names (gnutls_openpgp_crt_t key)
|
||||
{
|
||||
cdk_kbnode_t p, ctx;
|
||||
cdk_packet_t pkt;
|
||||
int nuids;
|
||||
|
||||
if (key == NULL)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx = NULL;
|
||||
nuids = 0;
|
||||
while ((p = cdk_kbnode_walk (key->knode, &ctx, 0)))
|
||||
{
|
||||
pkt = cdk_kbnode_get_packet (p);
|
||||
if (pkt->pkttype == CDK_PKT_USER_ID)
|
||||
nuids++;
|
||||
}
|
||||
|
||||
return nuids;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_crt_get_name - Extracts the userID
|
||||
* @key: the structure that contains the OpenPGP public key.
|
||||
* @idx: the index of the ID to extract
|
||||
* @buf: a pointer to a structure to hold the name
|
||||
* @sizeof_buf: holds the maximum size of @buf, on return hold the
|
||||
* actual/required size of @buf.
|
||||
*
|
||||
* Extracts the userID from the parsed OpenPGP key.
|
||||
*
|
||||
* Returns 0 on success, and GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
|
||||
* if the index of the ID does not exist.
|
||||
*
|
||||
**/
|
||||
int
|
||||
gnutls_openpgp_crt_get_name (gnutls_openpgp_crt_t key,
|
||||
int idx, char *buf, size_t * sizeof_buf)
|
||||
{
|
||||
cdk_kbnode_t ctx = NULL, p;
|
||||
cdk_packet_t pkt = NULL;
|
||||
cdk_pkt_userid_t uid = NULL;
|
||||
int pos = 0;
|
||||
|
||||
if (!key || !buf)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_INVALID_REQUEST;
|
||||
}
|
||||
|
||||
if (idx < 0 || idx > _gnutls_openpgp_count_key_names (key))
|
||||
return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
|
||||
|
||||
if (!idx)
|
||||
pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_USER_ID);
|
||||
else
|
||||
{
|
||||
pos = 0;
|
||||
while ((p = cdk_kbnode_walk (key->knode, &ctx, 0)))
|
||||
{
|
||||
pkt = cdk_kbnode_get_packet (p);
|
||||
if (pkt->pkttype == CDK_PKT_USER_ID && ++pos == idx)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pkt)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
uid = pkt->pkt.user_id;
|
||||
if (uid->len >= *sizeof_buf)
|
||||
{
|
||||
gnutls_assert ();
|
||||
*sizeof_buf = uid->len + 1;
|
||||
return GNUTLS_E_SHORT_MEMORY_BUFFER;
|
||||
}
|
||||
|
||||
memcpy (buf, uid->name, uid->len);
|
||||
buf[uid->len] = '\0'; /* make sure it's a string */
|
||||
*sizeof_buf = uid->len + 1;
|
||||
|
||||
if (uid->is_revoked)
|
||||
return GNUTLS_E_OPENPGP_UID_REVOKED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_crt_get_pk_algorithm - This function returns the key's PublicKey algorithm
|
||||
* @key: is an OpenPGP key
|
||||
* @bits: if bits is non null it will hold the size of the parameters' in bits
|
||||
*
|
||||
* This function will return the public key algorithm of an OpenPGP
|
||||
* certificate.
|
||||
*
|
||||
* If bits is non null, it should have enough size to hold the parameters
|
||||
* size in bits. For RSA the bits returned is the modulus.
|
||||
* For DSA the bits returned are of the public exponent.
|
||||
*
|
||||
* Returns a member of the GNUTLS_PKAlgorithm enumeration on success,
|
||||
* or a negative value on error.
|
||||
*
|
||||
**/
|
||||
gnutls_pk_algorithm_t
|
||||
gnutls_openpgp_crt_get_pk_algorithm (gnutls_openpgp_crt_t key,
|
||||
unsigned int *bits)
|
||||
{
|
||||
cdk_packet_t pkt;
|
||||
int algo;
|
||||
|
||||
if (!key)
|
||||
return MHD_GNUTLS_PK_UNKNOWN;
|
||||
|
||||
algo = 0;
|
||||
pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_PUBLIC_KEY);
|
||||
if (pkt && pkt->pkttype == CDK_PKT_PUBLIC_KEY)
|
||||
{
|
||||
if (bits)
|
||||
*bits = cdk_pk_get_nbits (pkt->pkt.public_key);
|
||||
algo = pkt->pkt.public_key->pubkey_algo;
|
||||
if (is_RSA (algo))
|
||||
algo = MHD_GNUTLS_PK_RSA;
|
||||
else
|
||||
algo = GNUTLS_E_UNKNOWN_PK_ALGORITHM;
|
||||
}
|
||||
|
||||
return algo;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_crt_get_version - Extracts the version of the key.
|
||||
* @key: the structure that contains the OpenPGP public key.
|
||||
*
|
||||
* Extract the version of the OpenPGP key.
|
||||
**/
|
||||
int
|
||||
gnutls_openpgp_crt_get_version (gnutls_openpgp_crt_t key)
|
||||
{
|
||||
cdk_packet_t pkt;
|
||||
int version;
|
||||
|
||||
if (!key)
|
||||
return -1;
|
||||
|
||||
pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_PUBLIC_KEY);
|
||||
if (pkt)
|
||||
version = pkt->pkt.public_key->version;
|
||||
else
|
||||
version = 0;
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_crt_get_creation_time - Extract the timestamp
|
||||
* @key: the structure that contains the OpenPGP public key.
|
||||
*
|
||||
* Returns the timestamp when the OpenPGP key was created.
|
||||
**/
|
||||
time_t
|
||||
gnutls_openpgp_crt_get_creation_time (gnutls_openpgp_crt_t key)
|
||||
{
|
||||
cdk_packet_t pkt;
|
||||
time_t timestamp;
|
||||
|
||||
if (!key)
|
||||
return (time_t) - 1;
|
||||
|
||||
pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_PUBLIC_KEY);
|
||||
if (pkt)
|
||||
timestamp = pkt->pkt.public_key->timestamp;
|
||||
else
|
||||
timestamp = 0;
|
||||
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_crt_get_expiration_time - Extract the expire date
|
||||
* @key: the structure that contains the OpenPGP public key.
|
||||
*
|
||||
* Returns the time when the OpenPGP key expires. A value of '0' means
|
||||
* that the key doesn't expire at all.
|
||||
**/
|
||||
time_t
|
||||
gnutls_openpgp_crt_get_expiration_time (gnutls_openpgp_crt_t key)
|
||||
{
|
||||
cdk_packet_t pkt;
|
||||
time_t expiredate;
|
||||
|
||||
if (!key)
|
||||
return (time_t) - 1;
|
||||
|
||||
pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_PUBLIC_KEY);
|
||||
if (pkt)
|
||||
expiredate = pkt->pkt.public_key->expiredate;
|
||||
else
|
||||
expiredate = 0;
|
||||
|
||||
return expiredate;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_crt_get_id - Gets the keyID
|
||||
* @key: the structure that contains the OpenPGP public key.
|
||||
* @keyid: the buffer to save the keyid.
|
||||
*
|
||||
* Returns the 64-bit keyID of the OpenPGP key.
|
||||
**/
|
||||
int
|
||||
gnutls_openpgp_crt_get_id (gnutls_openpgp_crt_t key, unsigned char keyid[8])
|
||||
{
|
||||
cdk_packet_t pkt;
|
||||
uint32_t kid[2];
|
||||
|
||||
if (!key || !keyid)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_INVALID_REQUEST;
|
||||
}
|
||||
|
||||
pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_PUBLIC_KEY);
|
||||
if (!pkt)
|
||||
return GNUTLS_E_OPENPGP_GETKEY_FAILED;
|
||||
|
||||
cdk_pk_get_keyid (pkt->pkt.public_key, kid);
|
||||
keyid[0] = kid[0] >> 24;
|
||||
keyid[1] = kid[0] >> 16;
|
||||
keyid[2] = kid[0] >> 8;
|
||||
keyid[3] = kid[0];
|
||||
keyid[4] = kid[1] >> 24;
|
||||
keyid[5] = kid[1] >> 16;
|
||||
keyid[6] = kid[1] >> 8;
|
||||
keyid[7] = kid[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_crt_check_hostname - This function compares the given hostname with the hostname in the key
|
||||
* @key: should contain an gnutls_openpgp_crt_t structure
|
||||
* @hostname: A null terminated string that contains a DNS name
|
||||
*
|
||||
* This function will check if the given key's owner matches
|
||||
* the given hostname. This is a basic implementation of the matching
|
||||
* described in RFC2818 (HTTPS), which takes into account wildcards.
|
||||
*
|
||||
* Returns non zero on success, and zero on failure.
|
||||
*
|
||||
**/
|
||||
int
|
||||
gnutls_openpgp_crt_check_hostname (gnutls_openpgp_crt_t key,
|
||||
const char *hostname)
|
||||
{
|
||||
char dnsname[MAX_CN];
|
||||
size_t dnsnamesize;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
/* Check through all included names. */
|
||||
for (i = 0; !(ret < 0); i++)
|
||||
{
|
||||
dnsnamesize = sizeof (dnsname);
|
||||
ret = gnutls_openpgp_crt_get_name (key, i, dnsname, &dnsnamesize);
|
||||
/* FIXME: ret is not used */
|
||||
if (_gnutls_hostname_compare (dnsname, hostname))
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* not found a matching name */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_crt_get_key_usage - This function returns the key's usage
|
||||
* @key: should contain a gnutls_openpgp_crt_t structure
|
||||
* @key_usage: where the key usage bits will be stored
|
||||
*
|
||||
* This function will return certificate's key usage, by checking the
|
||||
* key algorithm. The key usage value will ORed values of the:
|
||||
* GNUTLS_KEY_DIGITAL_SIGNATURE, GNUTLS_KEY_KEY_ENCIPHERMENT.
|
||||
*
|
||||
* A negative value may be returned in case of parsing error.
|
||||
*
|
||||
*/
|
||||
int
|
||||
gnutls_openpgp_crt_get_key_usage (gnutls_openpgp_crt_t key,
|
||||
unsigned int *key_usage)
|
||||
{
|
||||
cdk_packet_t pkt;
|
||||
int algo = 0;
|
||||
|
||||
if (!key)
|
||||
return GNUTLS_E_INVALID_REQUEST;
|
||||
|
||||
*key_usage = 0;
|
||||
|
||||
pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_PUBLIC_KEY);
|
||||
if (pkt && pkt->pkttype == CDK_PKT_PUBLIC_KEY)
|
||||
{
|
||||
algo = pkt->pkt.public_key->pubkey_algo;
|
||||
|
||||
/* FIXME: We need to take a look at the key flags because
|
||||
RSA-E and RSA-S are obsolete. Only RSA is used
|
||||
and the flags are used to set the capabilities. */
|
||||
if (is_DSA (algo) || algo == GCRY_PK_RSA_S)
|
||||
*key_usage |= KEY_DIGITAL_SIGNATURE;
|
||||
else if (algo == GCRY_PK_RSA_E)
|
||||
*key_usage |= KEY_KEY_ENCIPHERMENT;
|
||||
else if (algo == GCRY_PK_RSA)
|
||||
*key_usage |= KEY_DIGITAL_SIGNATURE | KEY_KEY_ENCIPHERMENT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation
|
||||
*
|
||||
* Author: Nikos Mavrogiannopoulos
|
||||
*
|
||||
* This file is part of GNUTLS-EXTRA.
|
||||
*
|
||||
* GNUTLS-EXTRA 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.
|
||||
*
|
||||
* GNUTLS-EXTRA is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Functions on OpenPGP privkey parsing
|
||||
*/
|
||||
|
||||
#include <gnutls_int.h>
|
||||
#include <gnutls_datum.h>
|
||||
#include <gnutls_global.h>
|
||||
#include <gnutls_errors.h>
|
||||
#include "openpgp.h"
|
||||
#include <gnutls_openpgp.h>
|
||||
#include <gnutls_cert.h>
|
||||
/* x509 */
|
||||
#include <rfc2818.h>
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_privkey_init - This function initializes a gnutls_openpgp_privkey_t structure
|
||||
* @key: The structure to be initialized
|
||||
*
|
||||
* This function will initialize an OpenPGP key structure.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*
|
||||
**/
|
||||
int
|
||||
gnutls_openpgp_privkey_init (gnutls_openpgp_privkey_t * key)
|
||||
{
|
||||
*key = gnutls_calloc (1, sizeof (gnutls_openpgp_privkey_int));
|
||||
|
||||
if (*key)
|
||||
return 0; /* success */
|
||||
return GNUTLS_E_MEMORY_ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_privkey_deinit - This function deinitializes memory used by a gnutls_openpgp_privkey_t structure
|
||||
* @key: The structure to be initialized
|
||||
*
|
||||
* This function will deinitialize a key structure.
|
||||
*
|
||||
**/
|
||||
void
|
||||
gnutls_openpgp_privkey_deinit (gnutls_openpgp_privkey_t key)
|
||||
{
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
mhd_gtls_gkey_deinit (&key->pkey);
|
||||
gnutls_free (key);
|
||||
}
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_privkey_import - This function will import a RAW or BASE64 encoded key
|
||||
* @key: The structure to store the parsed key.
|
||||
* @data: The RAW or BASE64 encoded key.
|
||||
* @format: One of gnutls_openpgp_crt_fmt_t elements.
|
||||
* @pass: Unused for now
|
||||
* @flags: should be zero
|
||||
*
|
||||
* This function will convert the given RAW or Base64 encoded key
|
||||
* to the native gnutls_openpgp_privkey_t format. The output will be stored in 'key'.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*
|
||||
**/
|
||||
int
|
||||
gnutls_openpgp_privkey_import (gnutls_openpgp_privkey_t key,
|
||||
const gnutls_datum_t * data,
|
||||
gnutls_openpgp_crt_fmt_t format,
|
||||
const char *pass, unsigned int flags)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = _gnutls_openpgp_raw_privkey_to_gkey (&key->pkey, data, format);
|
||||
if (rc)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_privkey_get_pk_algorithm - This function returns the key's PublicKey algorithm
|
||||
* @key: is an OpenPGP key
|
||||
* @bits: if bits is non null it will hold the size of the parameters' in bits
|
||||
*
|
||||
* This function will return the public key algorithm of an OpenPGP
|
||||
* certificate.
|
||||
*
|
||||
* If bits is non null, it should have enough size to hold the parameters
|
||||
* size in bits. For RSA the bits returned is the modulus.
|
||||
* For DSA the bits returned are of the public exponent.
|
||||
*
|
||||
* Returns a member of the GNUTLS_PKAlgorithm enumeration on success,
|
||||
* or a negative value on error.
|
||||
*
|
||||
**/
|
||||
gnutls_pk_algorithm_t
|
||||
gnutls_openpgp_privkey_get_pk_algorithm (gnutls_openpgp_privkey_t key,
|
||||
unsigned int *bits)
|
||||
{
|
||||
int pk = key->pkey.pk_algorithm;
|
||||
|
||||
if (bits)
|
||||
{
|
||||
*bits = 0;
|
||||
if (pk == MHD_GNUTLS_PK_RSA)
|
||||
*bits = _gnutls_mpi_get_nbits (key->pkey.params[0]);
|
||||
}
|
||||
|
||||
return pk;
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002, 2003, 2004, 2005, 2007 Free Software Foundation
|
||||
*
|
||||
* Author: Timo Schulz, Nikos Mavrogiannopoulos
|
||||
*
|
||||
* This file is part of GNUTLS-EXTRA.
|
||||
*
|
||||
* GNUTLS-EXTRA 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.
|
||||
*
|
||||
* GNUTLS-EXTRA is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Functions on OpenPGP key parsing
|
||||
*/
|
||||
|
||||
#include <gnutls_int.h>
|
||||
#include <gnutls_errors.h>
|
||||
#include <gnutls_openpgp.h>
|
||||
#include <gnutls_num.h>
|
||||
#include "openpgp.h"
|
||||
/* x509 */
|
||||
#include <verify.h> /* lib/x509/verify.h */
|
||||
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_crt_verify_ring - Verify all signatures in the key
|
||||
* @key: the structure that holds the key.
|
||||
* @keyring: holds the keyring to check against
|
||||
* @flags: unused (should be 0)
|
||||
* @verify: will hold the certificate verification output.
|
||||
*
|
||||
* Verify all signatures in the key, using the given set of keys (keyring).
|
||||
*
|
||||
* The key verification output will be put in @verify and will be
|
||||
* one or more of the gnutls_certificate_status_t enumerated elements bitwise or'd.
|
||||
*
|
||||
* GNUTLS_CERT_INVALID: A signature on the key is invalid.
|
||||
*
|
||||
* GNUTLS_CERT_REVOKED: The key has been revoked.
|
||||
*
|
||||
* Note that this function does not verify using any "web of
|
||||
* trust". You may use GnuPG for that purpose, or any other external
|
||||
* PGP application.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
**/
|
||||
int
|
||||
gnutls_openpgp_crt_verify_ring (gnutls_openpgp_crt_t key,
|
||||
gnutls_openpgp_keyring_t keyring,
|
||||
unsigned int flags, unsigned int *verify)
|
||||
{
|
||||
opaque id[8];
|
||||
cdk_error_t rc;
|
||||
int status;
|
||||
|
||||
if (!key || !keyring)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return GNUTLS_E_NO_CERTIFICATE_FOUND;
|
||||
}
|
||||
|
||||
*verify = 0;
|
||||
|
||||
rc = cdk_pk_check_sigs (key->knode, keyring->db, &status);
|
||||
if (rc == CDK_Error_No_Key)
|
||||
{
|
||||
rc = GNUTLS_E_NO_CERTIFICATE_FOUND;
|
||||
gnutls_assert ();
|
||||
return rc;
|
||||
}
|
||||
else if (rc != CDK_Success)
|
||||
{
|
||||
_gnutls_x509_log ("cdk_pk_check_sigs: error %d\n", rc);
|
||||
rc = _gnutls_map_cdk_rc (rc);
|
||||
gnutls_assert ();
|
||||
return rc;
|
||||
}
|
||||
_gnutls_x509_log ("status: %x\n", status);
|
||||
|
||||
if (status & CDK_KEY_INVALID)
|
||||
*verify |= GNUTLS_CERT_INVALID;
|
||||
if (status & CDK_KEY_REVOKED)
|
||||
*verify |= GNUTLS_CERT_REVOKED;
|
||||
if (status & CDK_KEY_NOSIGNER)
|
||||
*verify |= GNUTLS_CERT_SIGNER_NOT_FOUND;
|
||||
|
||||
/* Check if the key is included in the ring. */
|
||||
if (!(flags & GNUTLS_VERIFY_DO_NOT_ALLOW_SAME))
|
||||
{
|
||||
rc = gnutls_openpgp_crt_get_id (key, id);
|
||||
if (rc < 0)
|
||||
{
|
||||
gnutls_assert ();
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = gnutls_openpgp_keyring_check_id (keyring, id, 0);
|
||||
/* If it exists in the keyring don't treat it as unknown. */
|
||||
if (rc == 0 && *verify & GNUTLS_CERT_SIGNER_NOT_FOUND)
|
||||
*verify ^= GNUTLS_CERT_SIGNER_NOT_FOUND;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gnutls_openpgp_crt_verify_self - Verify the self signature on the key
|
||||
* @key: the structure that holds the key.
|
||||
* @flags: unused (should be 0)
|
||||
* @verify: will hold the key verification output.
|
||||
*
|
||||
* Verifies the self signature in the key.
|
||||
* The key verification output will be put in @verify and will be
|
||||
* one or more of the gnutls_certificate_status_t enumerated elements bitwise or'd.
|
||||
*
|
||||
* GNUTLS_CERT_INVALID: The self signature on the key is invalid.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
**/
|
||||
int
|
||||
gnutls_openpgp_crt_verify_self (gnutls_openpgp_crt_t key,
|
||||
unsigned int flags, unsigned int *verify)
|
||||
{
|
||||
int status;
|
||||
cdk_error_t rc;
|
||||
|
||||
rc = cdk_pk_check_self_sig (key->knode, &status);
|
||||
if (rc || status != CDK_KEY_VALID)
|
||||
*verify |= GNUTLS_CERT_INVALID;
|
||||
else
|
||||
*verify = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user