src/pgp_sequoia.c
author Neal H. Walfield <neal@pep.foundation>
Mon, 11 Mar 2019 15:39:57 +0100
branchsync
changeset 3332 1bdce679b688
parent 3272 4d6c07372e3e
child 3353 821f22f98225
permissions -rw-r--r--
Synchronize Sequoia port with upstream API changes
neal@3191
     1
// This file is under GNU General Public License 3.0
neal@3191
     2
// see LICENSE.txt
neal@3191
     3
neal@3191
     4
#define _GNU_SOURCE 1
neal@3191
     5
neal@3191
     6
#include "platform.h"
neal@3191
     7
#include "pEp_internal.h"
neal@3191
     8
#include "pgp_gpg.h"
neal@3191
     9
neal@3191
    10
#include <limits.h>
neal@3191
    11
#include <sys/stat.h>
neal@3191
    12
#include <sys/types.h>
neal@3191
    13
#include <error.h>
neal@3191
    14
neal@3191
    15
#include <sqlite3.h>
neal@3191
    16
neal@3191
    17
#include "wrappers.h"
neal@3191
    18
neal@3332
    19
#define TRACING 0
neal@3211
    20
#ifndef TRACING
neal@3211
    21
#  ifndef NDEBUG
neal@3211
    22
#    define TRACING 0
neal@3211
    23
#  else
neal@3211
    24
#    define TRACING 1
neal@3211
    25
#  endif
neal@3211
    26
#endif
neal@3211
    27
vb@3197
    28
// enable tracing if in debugging mode
neal@3211
    29
#if TRACING
neal@3191
    30
#  define _T(...) do {                          \
neal@3191
    31
        fprintf(stderr, ##__VA_ARGS__);         \
neal@3191
    32
    } while (0)
neal@3191
    33
#else
neal@3191
    34
#  define _T(...) do { } while (0)
neal@3191
    35
#endif
neal@3191
    36
neal@3191
    37
// Show the start of a tracepoint (i.e., don't print a newline).
neal@3191
    38
#define TC(...) do {       \
neal@3191
    39
    _T("%s: ", __func__);  \
neal@3191
    40
    _T(__VA_ARGS__);       \
neal@3191
    41
} while (0)
neal@3191
    42
neal@3191
    43
// Show a trace point.
neal@3191
    44
#  define T(...) do {  \
neal@3191
    45
    TC(__VA_ARGS__); \
neal@3191
    46
    _T("\n");          \
neal@3191
    47
} while(0)
neal@3191
    48
neal@3191
    49
// Verbosely displays errors.
neal@3332
    50
#  define DUMP_ERR(__de_err, __de_status, ...) do {             \
neal@3332
    51
    TC(__VA_ARGS__);                                            \
neal@3332
    52
    _T(": ");                                                   \
neal@3332
    53
    if (__de_err) {                                             \
neal@3332
    54
        _T("Sequoia: %s => ", pgp_error_to_string(__de_err));   \
neal@3332
    55
        pgp_error_free(__de_err);                               \
neal@3332
    56
    }                                                           \
neal@3332
    57
    _T("%s\n", pep_status_to_string(__de_status));              \
neal@3191
    58
} while(0)
neal@3191
    59
neal@3191
    60
// If __ec_status is an error, then disable the error, set 'status' to
neal@3191
    61
// it, and jump to 'out'.
neal@3332
    62
#define ERROR_OUT(__e_err, __ec_status, ...) do {                   \
neal@3191
    63
    PEP_STATUS ___ec_status = (__ec_status);                        \
neal@3191
    64
    if ((___ec_status) != PEP_STATUS_OK) {                          \
neal@3332
    65
        DUMP_ERR((__e_err), (___ec_status), ##__VA_ARGS__);         \
neal@3191
    66
        status = (___ec_status);                                    \
neal@3191
    67
        goto out;                                                   \
neal@3191
    68
    }                                                               \
neal@3191
    69
} while(0)
neal@3191
    70
neal@3191
    71
PEP_STATUS pgp_init(PEP_SESSION session, bool in_first)
neal@3191
    72
{
neal@3191
    73
    PEP_STATUS status = PEP_STATUS_OK;
neal@3191
    74
neal@3191
    75
    // Create the home directory.
neal@3191
    76
    char *home_env = getenv("HOME");
neal@3191
    77
    if (!home_env)
neal@3332
    78
        ERROR_OUT(NULL, PEP_INIT_GPGME_INIT_FAILED, "HOME unset");
neal@3191
    79
neal@3191
    80
    // Create the DB and initialize it.
neal@3191
    81
    char *path = NULL;
neal@3191
    82
    asprintf(&path, "%s/.pEp_keys.db", home_env);
neal@3191
    83
    if (!path)
neal@3332
    84
        ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "out of memory");
neal@3191
    85
neal@3191
    86
    int sqlite_result;
neal@3191
    87
    sqlite_result = sqlite3_open_v2(path,
neal@3191
    88
                                    &session->key_db,
neal@3191
    89
                                    SQLITE_OPEN_READWRITE
neal@3191
    90
                                    | SQLITE_OPEN_CREATE
neal@3191
    91
                                    | SQLITE_OPEN_FULLMUTEX
neal@3191
    92
                                    | SQLITE_OPEN_PRIVATECACHE,
neal@3191
    93
                                    NULL);
neal@3191
    94
    free(path);
neal@3191
    95
    if (sqlite_result != SQLITE_OK)
neal@3332
    96
        ERROR_OUT(NULL, PEP_INIT_CANNOT_OPEN_DB,
neal@3332
    97
                  "opening keys DB: %s", sqlite3_errmsg(session->key_db));
neal@3191
    98
neal@3191
    99
    sqlite_result = sqlite3_exec(session->key_db,
neal@3213
   100
                                 "PRAGMA secure_delete=true;\n"
neal@3213
   101
                                 "PRAGMA foreign_keys=true;\n"
neal@3191
   102
                                 "PRAGMA locking_mode=NORMAL;\n"
neal@3191
   103
                                 "PRAGMA journal_mode=WAL;\n",
neal@3191
   104
                                 NULL, NULL, NULL);
neal@3191
   105
    if (sqlite_result != SQLITE_OK)
neal@3332
   106
        ERROR_OUT(NULL, PEP_INIT_CANNOT_OPEN_DB,
neal@3191
   107
                  "setting pragmas: %s", sqlite3_errmsg(session->key_db));
neal@3191
   108
neal@3191
   109
    sqlite3_busy_timeout(session->key_db, BUSY_WAIT_TIME);
neal@3191
   110
neal@3191
   111
    sqlite_result = sqlite3_exec(session->key_db,
neal@3191
   112
                                 "CREATE TABLE IF NOT EXISTS keys (\n"
us@3209
   113
                                 "   primary_key TEXT UNIQUE PRIMARY KEY,\n"
us@3209
   114
                                 "   secret BOOLEAN,\n"
us@3209
   115
                                 "   tpk BLOB\n"
us@3209
   116
                                 ");\n"
us@3209
   117
                                 "CREATE INDEX IF NOT EXISTS keys_index\n"
us@3209
   118
                                 "  ON keys (primary_key, secret)\n",
neal@3191
   119
                                 NULL, NULL, NULL);
neal@3191
   120
    if (sqlite_result != SQLITE_OK)
neal@3332
   121
        ERROR_OUT(NULL, PEP_INIT_CANNOT_OPEN_DB,
neal@3191
   122
                  "creating keys table: %s",
neal@3191
   123
                  sqlite3_errmsg(session->key_db));
neal@3191
   124
neal@3191
   125
    sqlite_result = sqlite3_exec(session->key_db,
neal@3191
   126
                                 "CREATE TABLE IF NOT EXISTS subkeys (\n"
us@3209
   127
                                 "   subkey TEXT NOT NULL,\n"
us@3209
   128
                                 "   primary_key TEXT NOT NULL,\n"
us@3209
   129
                                 "   UNIQUE(subkey, primary_key),\n"
neal@3191
   130
                                 "   FOREIGN KEY (primary_key)\n"
neal@3191
   131
                                 "       REFERENCES keys(primary_key)\n"
neal@3191
   132
                                 "     ON DELETE CASCADE\n"
us@3209
   133
                                 ");\n"
us@3209
   134
                                 "CREATE INDEX IF NOT EXISTS subkeys_index\n"
us@3209
   135
                                 "  ON subkeys (subkey, primary_key)\n",
neal@3191
   136
                                 NULL, NULL, NULL);
neal@3191
   137
    if (sqlite_result != SQLITE_OK)
neal@3332
   138
        ERROR_OUT(NULL, PEP_INIT_CANNOT_OPEN_DB,
neal@3191
   139
                  "creating subkeys table: %s",
neal@3191
   140
                  sqlite3_errmsg(session->key_db));
neal@3191
   141
us@3209
   142
    sqlite_result = sqlite3_exec(session->key_db,
us@3209
   143
                                 "CREATE TABLE IF NOT EXISTS userids (\n"
us@3209
   144
                                 "   userid TEXT NOT NULL,\n"
us@3209
   145
                                 "   primary_key TEXT NOT NULL,\n"
us@3209
   146
                                 "   UNIQUE(userid, primary_key),\n"
us@3209
   147
                                 "   FOREIGN KEY (primary_key)\n"
us@3209
   148
                                 "       REFERENCES keys(primary_key)\n"
us@3209
   149
                                 "     ON DELETE CASCADE\n"
us@3209
   150
                                 ");\n"
us@3209
   151
                                 "CREATE INDEX IF NOT EXISTS userids_index\n"
us@3209
   152
                                 "  ON userids (userid, primary_key)\n",
us@3209
   153
                                 NULL, NULL, NULL);
us@3209
   154
    if (sqlite_result != SQLITE_OK)
neal@3332
   155
        ERROR_OUT(NULL, PEP_INIT_CANNOT_OPEN_DB,
us@3209
   156
                  "creating userids table: %s",
us@3209
   157
                  sqlite3_errmsg(session->key_db));
us@3209
   158
neal@3191
   159
    sqlite_result
neal@3191
   160
        = sqlite3_prepare_v2(session->key_db, "begin transaction",
neal@3212
   161
                             -1, &session->sq_sql.begin_transaction, NULL);
neal@3191
   162
    assert(sqlite_result == SQLITE_OK);
neal@3191
   163
neal@3191
   164
    sqlite_result
neal@3191
   165
        = sqlite3_prepare_v2(session->key_db, "commit transaction",
neal@3212
   166
                             -1, &session->sq_sql.commit_transaction, NULL);
neal@3191
   167
    assert(sqlite_result == SQLITE_OK);
neal@3191
   168
neal@3191
   169
    sqlite_result
neal@3191
   170
        = sqlite3_prepare_v2(session->key_db, "rollback transaction",
neal@3212
   171
                             -1, &session->sq_sql.rollback_transaction, NULL);
neal@3191
   172
    assert(sqlite_result == SQLITE_OK);
neal@3191
   173
neal@3191
   174
    sqlite_result
neal@3191
   175
        = sqlite3_prepare_v2(session->key_db,
us@3209
   176
                             "SELECT tpk, secret FROM keys"
us@3209
   177
                             " WHERE primary_key == ?",
neal@3212
   178
                             -1, &session->sq_sql.tpk_find, NULL);
us@3209
   179
    assert(sqlite_result == SQLITE_OK);
us@3209
   180
us@3209
   181
    sqlite_result
us@3209
   182
        = sqlite3_prepare_v2(session->key_db,
us@3209
   183
                             "SELECT tpk, secret FROM keys"
us@3209
   184
                             " WHERE primary_key == ? and secret == 1",
neal@3212
   185
                             -1, &session->sq_sql.tsk_find, NULL);
us@3209
   186
    assert(sqlite_result == SQLITE_OK);
us@3209
   187
us@3209
   188
    sqlite_result
us@3209
   189
        = sqlite3_prepare_v2(session->key_db,
us@3209
   190
                             "SELECT tpk, secret FROM subkeys"
us@3209
   191
                             " LEFT JOIN keys"
us@3209
   192
                             "  ON subkeys.primary_key == keys.primary_key"
us@3209
   193
                             " WHERE subkey == ?",
neal@3212
   194
                             -1, &session->sq_sql.tpk_find_by_keyid, NULL);
us@3209
   195
    assert(sqlite_result == SQLITE_OK);
us@3209
   196
us@3209
   197
    sqlite_result
us@3209
   198
        = sqlite3_prepare_v2(session->key_db,
us@3209
   199
                             "SELECT tpk, secret FROM subkeys"
us@3209
   200
                             " LEFT JOIN keys"
us@3209
   201
                             "  ON subkeys.primary_key == keys.primary_key"
us@3209
   202
                             " WHERE subkey == ?",
neal@3212
   203
                             -1, &session->sq_sql.tpk_find_by_keyid, NULL);
us@3209
   204
    assert(sqlite_result == SQLITE_OK);
us@3209
   205
us@3209
   206
    sqlite_result
us@3209
   207
        = sqlite3_prepare_v2(session->key_db,
us@3209
   208
                             "SELECT tpk, secret FROM subkeys"
us@3209
   209
                             " LEFT JOIN keys"
us@3209
   210
                             "  ON subkeys.primary_key == keys.primary_key"
us@3209
   211
                             " WHERE subkey == ? and keys.secret == 1",
neal@3212
   212
                             -1, &session->sq_sql.tsk_find_by_keyid, NULL);
us@3209
   213
    assert(sqlite_result == SQLITE_OK);
us@3209
   214
us@3209
   215
    sqlite_result
us@3209
   216
        = sqlite3_prepare_v2(session->key_db,
us@3209
   217
                             "SELECT tpk, secret FROM userids"
us@3209
   218
                             " LEFT JOIN keys"
us@3209
   219
                             "  ON userids.primary_key == keys.primary_key"
us@3209
   220
                             " WHERE userid == ?",
neal@3212
   221
                             -1, &session->sq_sql.tpk_find_by_email, NULL);
us@3209
   222
    assert(sqlite_result == SQLITE_OK);
us@3209
   223
us@3209
   224
    sqlite_result
us@3209
   225
        = sqlite3_prepare_v2(session->key_db,
us@3209
   226
                             "SELECT tpk, secret FROM userids"
us@3209
   227
                             " LEFT JOIN keys"
us@3209
   228
                             "  ON userids.primary_key == keys.primary_key"
us@3209
   229
                             " WHERE userid == ? and keys.secret == 1",
neal@3212
   230
                             -1, &session->sq_sql.tsk_find_by_email, NULL);
us@3209
   231
    assert(sqlite_result == SQLITE_OK);
us@3209
   232
us@3209
   233
    sqlite_result
us@3209
   234
        = sqlite3_prepare_v2(session->key_db,
us@3209
   235
                             "select tpk, secret from keys",
neal@3212
   236
                             -1, &session->sq_sql.tpk_all, NULL);
us@3209
   237
    assert(sqlite_result == SQLITE_OK);
us@3209
   238
us@3209
   239
    sqlite_result
us@3209
   240
        = sqlite3_prepare_v2(session->key_db,
us@3209
   241
                             "select tpk, secret from keys where secret = 1",
neal@3212
   242
                             -1, &session->sq_sql.tsk_all, NULL);
us@3209
   243
    assert(sqlite_result == SQLITE_OK);
us@3209
   244
us@3209
   245
    sqlite_result
us@3209
   246
        = sqlite3_prepare_v2(session->key_db,
neal@3191
   247
                             "INSERT OR REPLACE INTO keys"
us@3209
   248
                             "   (primary_key, secret, tpk)"
us@3209
   249
                             " VALUES (?, ?, ?)",
neal@3212
   250
                             -1, &session->sq_sql.tpk_save_insert_primary, NULL);
neal@3191
   251
    assert(sqlite_result == SQLITE_OK);
neal@3191
   252
neal@3191
   253
    sqlite_result
neal@3191
   254
        = sqlite3_prepare_v2(session->key_db,
neal@3191
   255
                             "INSERT OR REPLACE INTO subkeys"
neal@3191
   256
                             "   (subkey, primary_key)"
neal@3191
   257
                             " VALUES (?, ?)",
neal@3212
   258
                             -1, &session->sq_sql.tpk_save_insert_subkeys, NULL);
neal@3191
   259
    assert(sqlite_result == SQLITE_OK);
neal@3191
   260
neal@3191
   261
    sqlite_result
neal@3191
   262
        = sqlite3_prepare_v2(session->key_db,
us@3209
   263
                             "INSERT OR REPLACE INTO userids"
us@3209
   264
                             "   (userid, primary_key)"
us@3209
   265
                             " VALUES (?, ?)",
neal@3212
   266
                             -1, &session->sq_sql.tpk_save_insert_userids, NULL);
neal@3191
   267
    assert(sqlite_result == SQLITE_OK);
neal@3191
   268
neal@3191
   269
 out:
neal@3191
   270
    if (status != PEP_STATUS_OK)
neal@3191
   271
        pgp_release(session, in_first);
neal@3191
   272
    return status;
neal@3191
   273
}
neal@3191
   274
neal@3191
   275
void pgp_release(PEP_SESSION session, bool out_last)
neal@3191
   276
{
neal@3212
   277
    sqlite3_stmt **stmts = (sqlite3_stmt **) &session->sq_sql;
neal@3212
   278
    for (int i = 0; i < sizeof(session->sq_sql) / sizeof(*stmts); i ++)
neal@3212
   279
        if (stmts[i]) {
neal@3212
   280
            sqlite3_finalize(stmts[i]);
neal@3212
   281
            stmts[i] = NULL;
neal@3212
   282
        }
neal@3191
   283
neal@3191
   284
    if (session->key_db) {
neal@3191
   285
        int result = sqlite3_close_v2(session->key_db);
neal@3191
   286
        if (result != 0)
neal@3332
   287
            DUMP_ERR(NULL, PEP_UNKNOWN_ERROR,
neal@3191
   288
                     "Closing key DB: sqlite3_close_v2: %s",
neal@3191
   289
                     sqlite3_errstr(result));
neal@3191
   290
        session->key_db = NULL;
neal@3191
   291
    }
neal@3191
   292
}
neal@3191
   293
neal@3191
   294
// Ensures that a fingerprint is in canonical form.  A canonical
neal@3191
   295
// fingerprint doesn't contain any white space.
neal@3191
   296
//
neal@3191
   297
// This function does *not* consume fpr.
neal@3332
   298
static char *pgp_fingerprint_canonicalize(const char *) __attribute__((nonnull));
neal@3332
   299
static char *pgp_fingerprint_canonicalize(const char *fpr)
neal@3191
   300
{
neal@3332
   301
    pgp_fingerprint_t pgp_fpr = pgp_fingerprint_from_hex(fpr);
neal@3332
   302
    char *fpr_canonicalized = pgp_fingerprint_to_hex(pgp_fpr);
neal@3332
   303
    pgp_fingerprint_free(pgp_fpr);
neal@3191
   304
neal@3191
   305
    return fpr_canonicalized;
neal@3191
   306
}
neal@3191
   307
neal@3191
   308
// Splits an OpenPGP user id into its name and email components.  A
neal@3191
   309
// user id looks like:
neal@3191
   310
//
neal@3191
   311
//   Name (comment) <email>
neal@3191
   312
//
neal@3191
   313
// This function takes ownership of user_id!!!
neal@3191
   314
//
neal@3191
   315
// namep and emailp may be NULL if they are not required.
neal@3191
   316
static void user_id_split(char *, char **, char **) __attribute__((nonnull(1)));
neal@3191
   317
static void user_id_split(char *user_id, char **namep, char **emailp)
neal@3191
   318
{
neal@3191
   319
    if (namep)
neal@3191
   320
        *namep = NULL;
neal@3191
   321
    if (emailp)
neal@3191
   322
        *emailp = NULL;
neal@3191
   323
neal@3191
   324
    char *email = strchr(user_id, '<');
neal@3191
   325
    if (email) {
neal@3191
   326
        // NUL terminate the string here so that user_id now points at
neal@3191
   327
        // most to: "Name (comment)"
neal@3191
   328
        *email = 0;
neal@3191
   329
neal@3191
   330
        if (emailp && email[1]) {
neal@3191
   331
            email = email + 1;
neal@3191
   332
            char *end = strchr(email, '>');
neal@3191
   333
            if (end) {
neal@3191
   334
                *end = 0;
neal@3191
   335
                *emailp = strdup(email);
neal@3191
   336
            }
neal@3191
   337
        }
neal@3191
   338
    }
neal@3191
   339
neal@3191
   340
    if (!namep)
neal@3191
   341
        return;
neal@3191
   342
neal@3191
   343
    char *comment = strchr(user_id, '(');
neal@3191
   344
    if (comment)
neal@3191
   345
        *comment = 0;
neal@3191
   346
neal@3191
   347
    // Kill any trailing white space.
neal@3191
   348
    for (size_t l = strlen(user_id); l > 0 && user_id[l - 1] == ' '; l --)
neal@3191
   349
        user_id[l - 1] = 0;
neal@3191
   350
neal@3191
   351
    // Kill any leading whitespace.
neal@3191
   352
    char *start = user_id;
neal@3191
   353
    while (*start == ' ')
neal@3191
   354
        start ++;
neal@3191
   355
    if (start[0])
neal@3191
   356
        *namep = strdup(start);
neal@3191
   357
neal@3191
   358
    free(user_id);
neal@3191
   359
}
neal@3191
   360
us@3209
   361
// step statement and load the tpk and secret.
neal@3332
   362
static PEP_STATUS key_load(PEP_SESSION, sqlite3_stmt *, pgp_tpk_t *, int *)
us@3209
   363
    __attribute__((nonnull(1, 2)));
us@3209
   364
static PEP_STATUS key_load(PEP_SESSION session, sqlite3_stmt *stmt,
neal@3332
   365
                           pgp_tpk_t *tpkp, int *secretp)
neal@3191
   366
{
neal@3191
   367
    PEP_STATUS status = PEP_STATUS_OK;
neal@3191
   368
    int sqlite_result = sqlite3_step(stmt);
neal@3191
   369
    switch (sqlite_result) {
neal@3191
   370
    case SQLITE_ROW:
us@3209
   371
        if (tpkp) {
neal@3191
   372
            int data_len = sqlite3_column_bytes(stmt, 0);
neal@3191
   373
            const void *data = sqlite3_column_blob(stmt, 0);
neal@3191
   374
neal@3332
   375
            pgp_error_t err = NULL;
neal@3332
   376
            *tpkp = pgp_tpk_from_bytes(&err, data, data_len);
us@3209
   377
            if (!*tpkp)
neal@3332
   378
                ERROR_OUT(err, PEP_GET_KEY_FAILED, "parsing TPK");
us@3209
   379
        }
neal@3191
   380
us@3209
   381
        if (secretp)
us@3209
   382
            *secretp = sqlite3_column_int(stmt, 1);
us@3209
   383
neal@3191
   384
        break;
neal@3191
   385
    case SQLITE_DONE:
neal@3191
   386
        // Got nothing.
neal@3191
   387
        status = PEP_KEY_NOT_FOUND;
neal@3191
   388
        break;
neal@3191
   389
    default:
neal@3332
   390
        ERROR_OUT(NULL, PEP_UNKNOWN_ERROR,
us@3209
   391
                  "stepping: %s", sqlite3_errmsg(session->key_db));
neal@3191
   392
    }
neal@3191
   393
neal@3191
   394
 out:
us@3209
   395
    T(" -> %s", pep_status_to_string(status));
us@3209
   396
    return status;
us@3209
   397
}
us@3209
   398
us@3209
   399
// step statement until exhausted and load the tpks.
neal@3332
   400
static PEP_STATUS key_loadn(PEP_SESSION, sqlite3_stmt *, pgp_tpk_t **, int *)
us@3209
   401
    __attribute__((nonnull));
us@3209
   402
static PEP_STATUS key_loadn(PEP_SESSION session, sqlite3_stmt *stmt,
neal@3332
   403
                            pgp_tpk_t **tpksp, int *tpks_countp)
us@3209
   404
{
us@3209
   405
    PEP_STATUS status = PEP_STATUS_OK;
us@3209
   406
    int tpks_count = 0;
us@3209
   407
    int tpks_capacity = 8;
neal@3332
   408
    pgp_tpk_t *tpks = calloc(tpks_capacity, sizeof(pgp_tpk_t));
us@3209
   409
    if (!tpks)
neal@3332
   410
        ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "out of memory");
us@3209
   411
us@3209
   412
    for (;;) {
neal@3332
   413
        pgp_tpk_t tpk = NULL;
us@3209
   414
        status = key_load(session, stmt, &tpk, NULL);
us@3209
   415
        if (status == PEP_KEY_NOT_FOUND) {
us@3209
   416
            status = PEP_STATUS_OK;
us@3209
   417
            break;
us@3209
   418
        }
neal@3332
   419
        ERROR_OUT(NULL, status, "loading TPK");
us@3209
   420
us@3209
   421
        if (tpks_count == tpks_capacity) {
us@3209
   422
            tpks_capacity *= 2;
us@3209
   423
            tpks = realloc(tpks, sizeof(tpks[0]) * tpks_capacity);
us@3209
   424
            if (!tpks)
neal@3332
   425
                ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "tpks");
us@3209
   426
        }
us@3209
   427
        tpks[tpks_count ++] = tpk;
us@3209
   428
    }
us@3209
   429
us@3209
   430
 out:
us@3209
   431
    if (status != PEP_STATUS_OK) {
us@3209
   432
        for (int i = 0; i < tpks_count; i ++)
neal@3332
   433
            pgp_tpk_free(tpks[i]);
us@3209
   434
        free(tpks);
us@3209
   435
    } else {
us@3209
   436
        *tpksp = tpks;
us@3209
   437
        *tpks_countp = tpks_count;
us@3209
   438
    }
us@3209
   439
us@3209
   440
    T(" -> %s (%d tpks)", pep_status_to_string(status), *tpks_countp);
us@3209
   441
    return status;
us@3209
   442
}
us@3209
   443
us@3209
   444
// Returns the TPK identified by the provided fingerprint.
us@3209
   445
//
us@3209
   446
// This function only matches on the primary key!
neal@3332
   447
static PEP_STATUS tpk_find(PEP_SESSION, pgp_fingerprint_t, int, pgp_tpk_t *, int *)
us@3209
   448
    __attribute__((nonnull(1, 2)));
us@3209
   449
static PEP_STATUS tpk_find(PEP_SESSION session,
neal@3332
   450
                           pgp_fingerprint_t fpr, int private_only,
neal@3332
   451
                           pgp_tpk_t *tpk, int *secret)
us@3209
   452
{
us@3209
   453
    PEP_STATUS status = PEP_STATUS_OK;
neal@3332
   454
    char *fpr_str = pgp_fingerprint_to_hex(fpr);
us@3209
   455
us@3209
   456
    T("(%s, %d)", fpr_str, private_only);
us@3209
   457
neal@3332
   458
    sqlite3_stmt *stmt
neal@3332
   459
        = private_only ? session->sq_sql.tsk_find : session->sq_sql.tpk_find;
us@3209
   460
    sqlite3_bind_text(stmt, 1, fpr_str, -1, SQLITE_STATIC);
us@3209
   461
us@3209
   462
    status = key_load(session, stmt, tpk, secret);
neal@3332
   463
    ERROR_OUT(NULL, status, "Looking up %s", fpr_str);
us@3209
   464
us@3209
   465
 out:
neal@3191
   466
    sqlite3_reset(stmt);
us@3209
   467
    T("(%s, %d) -> %s", fpr_str, private_only, pep_status_to_string(status));
us@3209
   468
    free(fpr_str);
neal@3191
   469
    return status;
neal@3191
   470
}
neal@3191
   471
us@3209
   472
// Returns the TPK identified by the provided keyid.
us@3209
   473
//
us@3209
   474
// This function matches on both primary keys and subkeys!
us@3209
   475
//
us@3209
   476
// Note: There can be multiple TPKs for a given keyid.  This can
us@3209
   477
// occur, because an encryption subkey can be bound to multiple TPKs.
us@3209
   478
// Also, it is possible to collide key ids.  If there are multiple key
us@3209
   479
// ids for a given key, this just returns one of them.
us@3209
   480
//
us@3209
   481
// If private_only is set, this will only consider TPKs with some
us@3209
   482
// secret key material.
neal@3332
   483
static PEP_STATUS tpk_find_by_keyid_hex(PEP_SESSION, const char *, int, pgp_tpk_t *, int *)
us@3209
   484
  __attribute__((nonnull(1, 2)));
us@3209
   485
static PEP_STATUS tpk_find_by_keyid_hex(
us@3209
   486
        PEP_SESSION session, const char *keyid_hex, int private_only,
neal@3332
   487
        pgp_tpk_t *tpkp, int *secretp)
us@3209
   488
{
us@3209
   489
    PEP_STATUS status = PEP_STATUS_OK;
us@3209
   490
    T("(%s, %d)", keyid_hex, private_only);
us@3209
   491
us@3209
   492
    sqlite3_stmt *stmt
neal@3212
   493
        = private_only ? session->sq_sql.tsk_find_by_keyid : session->sq_sql.tpk_find_by_keyid;
us@3209
   494
    sqlite3_bind_text(stmt, 1, keyid_hex, -1, SQLITE_STATIC);
us@3209
   495
us@3209
   496
    status = key_load(session, stmt, tpkp, secretp);
neal@3332
   497
    ERROR_OUT(NULL, status, "Looking up %s", keyid_hex);
us@3209
   498
us@3209
   499
 out:
us@3209
   500
    sqlite3_reset(stmt);
us@3209
   501
    T("(%s, %d) -> %s", keyid_hex, private_only, pep_status_to_string(status));
us@3209
   502
    return status;
us@3209
   503
}
us@3209
   504
us@3209
   505
// See tpk_find_by_keyid_hex.
neal@3332
   506
PEP_STATUS tpk_find_by_keyid(PEP_SESSION, pgp_keyid_t, int, pgp_tpk_t *, int *)
neal@3191
   507
    __attribute__((nonnull(1, 2)));
us@3209
   508
PEP_STATUS tpk_find_by_keyid(PEP_SESSION session,
neal@3332
   509
                             pgp_keyid_t keyid, int private_only,
neal@3332
   510
                             pgp_tpk_t *tpkp, int *secretp)
neal@3191
   511
{
neal@3332
   512
    char *keyid_hex = pgp_keyid_to_hex(keyid);
neal@3191
   513
    if (! keyid_hex)
neal@3191
   514
        return PEP_OUT_OF_MEMORY;
us@3209
   515
    PEP_STATUS status
us@3209
   516
        = tpk_find_by_keyid_hex(session, keyid_hex, private_only, tpkp, secretp);
neal@3191
   517
    free(keyid_hex);
neal@3191
   518
    return status;
neal@3191
   519
}
neal@3191
   520
us@3209
   521
// See tpk_find_by_keyid_hex.
neal@3332
   522
static PEP_STATUS tpk_find_by_fpr(PEP_SESSION, pgp_fingerprint_t, int,
neal@3332
   523
                                  pgp_tpk_t *, int *)
neal@3191
   524
    __attribute__((nonnull(1, 2)));
us@3209
   525
static PEP_STATUS tpk_find_by_fpr(
neal@3332
   526
    PEP_SESSION session, pgp_fingerprint_t fpr, int private_only,
neal@3332
   527
    pgp_tpk_t *tpkp, int *secretp)
neal@3191
   528
{
neal@3332
   529
    pgp_keyid_t keyid = pgp_fingerprint_to_keyid(fpr);
neal@3191
   530
    if (! keyid)
neal@3191
   531
        return PEP_OUT_OF_MEMORY;
us@3209
   532
    PEP_STATUS status
us@3209
   533
        = tpk_find_by_keyid(session, keyid, private_only, tpkp, secretp);
neal@3332
   534
    pgp_keyid_free(keyid);
neal@3191
   535
    return status;
neal@3191
   536
}
neal@3191
   537
us@3209
   538
// See tpk_find_by_keyid_hex.
neal@3332
   539
static PEP_STATUS tpk_find_by_fpr_hex(PEP_SESSION, const char *, int, pgp_tpk_t *, int *secret)
neal@3191
   540
    __attribute__((nonnull(1, 2)));
us@3209
   541
static PEP_STATUS tpk_find_by_fpr_hex(
us@3209
   542
    PEP_SESSION session, const char *fpr, int private_only,
neal@3332
   543
    pgp_tpk_t *tpkp, int *secretp)
neal@3191
   544
{
neal@3332
   545
    pgp_fingerprint_t pgp_fpr = pgp_fingerprint_from_hex(fpr);
neal@3332
   546
    if (! pgp_fpr)
neal@3191
   547
        return PEP_OUT_OF_MEMORY;
us@3209
   548
    PEP_STATUS status
neal@3332
   549
        = tpk_find_by_fpr(session, pgp_fpr, private_only, tpkp, secretp);
neal@3332
   550
    pgp_fingerprint_free(pgp_fpr);
neal@3191
   551
    return status;
neal@3191
   552
}
neal@3191
   553
us@3209
   554
// Returns all known TPKs.
neal@3332
   555
static PEP_STATUS tpk_all(PEP_SESSION, int, pgp_tpk_t **, int *) __attribute__((nonnull));
us@3209
   556
static PEP_STATUS tpk_all(PEP_SESSION session, int private_only,
neal@3332
   557
                          pgp_tpk_t **tpksp, int *tpks_countp) {
us@3209
   558
    PEP_STATUS status = PEP_STATUS_OK;
neal@3212
   559
    sqlite3_stmt *stmt = private_only ? session->sq_sql.tsk_all : session->sq_sql.tpk_all;
us@3209
   560
    status = key_loadn(session, stmt, tpksp, tpks_countp);
neal@3332
   561
    ERROR_OUT(NULL, status, "loading TPKs");
us@3209
   562
 out:
us@3209
   563
    sqlite3_reset(stmt);
us@3209
   564
    return status;
us@3209
   565
}
us@3209
   566
us@3209
   567
// Returns keys that have a user id that matches the specified pattern.
us@3209
   568
//
neal@3332
   569
// The keys returned must be freed using pgp_tpk_free.
neal@3332
   570
static PEP_STATUS tpk_find_by_email(PEP_SESSION, const char *, int, pgp_tpk_t **, int *)
us@3209
   571
    __attribute__((nonnull));
us@3209
   572
static PEP_STATUS tpk_find_by_email(PEP_SESSION session,
us@3209
   573
                                    const char *pattern, int private_only,
neal@3332
   574
                                    pgp_tpk_t **tpksp, int *countp)
us@3209
   575
{
us@3209
   576
    PEP_STATUS status = PEP_STATUS_OK;
us@3209
   577
    T("(%s)", pattern);
us@3209
   578
us@3209
   579
    sqlite3_stmt *stmt
neal@3212
   580
        = private_only ? session->sq_sql.tsk_find_by_email : session->sq_sql.tpk_find_by_email;
us@3209
   581
    sqlite3_bind_text(stmt, 1, pattern, -1, SQLITE_STATIC);
us@3209
   582
us@3209
   583
    status = key_loadn(session, stmt, tpksp, countp);
neal@3332
   584
    ERROR_OUT(NULL, status, "Searching for '%s'", pattern);
us@3209
   585
us@3209
   586
 out:
us@3209
   587
    sqlite3_reset(stmt);
us@3209
   588
    T("(%s) -> %s (%d results)", pattern, pep_status_to_string(status), *countp);
us@3209
   589
    return status;
us@3209
   590
}
us@3209
   591
neal@3191
   592
us@3209
   593
// Saves the specified TPK.
neal@3191
   594
//
us@3209
   595
// This function takes ownership of TPK.
neal@3332
   596
static PEP_STATUS tpk_save(PEP_SESSION, pgp_tpk_t, identity_list **)
us@3209
   597
    __attribute__((nonnull(1, 2)));
neal@3332
   598
static PEP_STATUS tpk_save(PEP_SESSION session, pgp_tpk_t tpk,
us@3209
   599
                           identity_list **private_idents)
neal@3191
   600
{
neal@3191
   601
    PEP_STATUS status = PEP_STATUS_OK;
neal@3332
   602
    pgp_error_t err = NULL;
neal@3332
   603
    pgp_fingerprint_t pgp_fpr = NULL;
neal@3191
   604
    char *fpr = NULL;
neal@3191
   605
    void *tsk_buffer = NULL;
neal@3191
   606
    size_t tsk_buffer_len = 0;
neal@3191
   607
    int tried_commit = 0;
neal@3332
   608
    pgp_tpk_key_iter_t key_iter = NULL;
neal@3332
   609
    pgp_user_id_binding_iter_t user_id_iter = NULL;
neal@3191
   610
neal@3332
   611
    pgp_fpr = pgp_tpk_fingerprint(tpk);
neal@3332
   612
    fpr = pgp_fingerprint_to_hex(pgp_fpr);
us@3209
   613
    T("(%s, private_idents: %s)", fpr, private_idents ? "yes" : "no");
neal@3191
   614
us@3209
   615
    // Merge any existing data into TPK.
neal@3332
   616
    pgp_tpk_t current = NULL;
neal@3332
   617
    status = tpk_find(session, pgp_fpr, false, &current, NULL);
neal@3191
   618
    if (status == PEP_KEY_NOT_FOUND)
neal@3191
   619
        status = PEP_STATUS_OK;
neal@3191
   620
    else
neal@3332
   621
        ERROR_OUT(NULL, status, "Looking up %s", fpr);
neal@3332
   622
    if (current) {
neal@3332
   623
        tpk = pgp_tpk_merge(&err, tpk, current);
neal@3332
   624
        if (! tpk)
neal@3332
   625
            ERROR_OUT(err, PEP_UNKNOWN_ERROR, "Merging TPKs");
neal@3332
   626
    }
neal@3191
   627
neal@3332
   628
    int is_tsk = pgp_tpk_is_tsk(tpk);
neal@3191
   629
neal@3191
   630
    // Serialize it.
neal@3332
   631
    pgp_writer_t writer = pgp_writer_alloc(&tsk_buffer, &tsk_buffer_len);
neal@3191
   632
    if (! writer)
neal@3332
   633
        ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "out of memory");
neal@3191
   634
neal@3332
   635
    pgp_status_t pgp_status;
neal@3332
   636
    pgp_tsk_t tsk = pgp_tpk_into_tsk(tpk);
neal@3332
   637
    pgp_status = pgp_tsk_serialize(&err, tsk, writer);
neal@3332
   638
    tpk = pgp_tsk_into_tpk(tsk);
neal@3332
   639
    //pgp_writer_free(writer);
neal@3332
   640
    if (pgp_status != 0)
neal@3332
   641
        ERROR_OUT(err, PEP_UNKNOWN_ERROR, "Serializing TPK");
neal@3191
   642
neal@3191
   643
neal@3191
   644
    // Insert the TSK into the DB.
neal@3212
   645
    sqlite3_stmt *stmt = session->sq_sql.begin_transaction;
neal@3191
   646
    int sqlite_result = sqlite3_step(stmt);
neal@3191
   647
    sqlite3_reset(stmt);
neal@3191
   648
    if (sqlite_result != SQLITE_DONE)
neal@3332
   649
        ERROR_OUT(NULL, PEP_UNKNOWN_ERROR,
neal@3191
   650
                  "begin transaction failed: %s",
neal@3191
   651
                  sqlite3_errmsg(session->key_db));
neal@3191
   652
neal@3212
   653
    stmt = session->sq_sql.tpk_save_insert_primary;
neal@3191
   654
    sqlite3_bind_text(stmt, 1, fpr, -1, SQLITE_STATIC);
us@3209
   655
    sqlite3_bind_int(stmt, 2, is_tsk);
us@3209
   656
    sqlite3_bind_blob(stmt, 3, tsk_buffer, tsk_buffer_len, SQLITE_STATIC);
neal@3191
   657
neal@3191
   658
    sqlite_result = sqlite3_step(stmt);
neal@3191
   659
    sqlite3_reset(stmt);
neal@3191
   660
    if (sqlite_result != SQLITE_DONE)
neal@3332
   661
        ERROR_OUT(NULL, PEP_UNKNOWN_ERROR,
us@3209
   662
                  "Saving TPK: %s", sqlite3_errmsg(session->key_db));
neal@3191
   663
neal@3191
   664
    // Insert the "subkeys" (the primary key and the subkeys).
neal@3212
   665
    stmt = session->sq_sql.tpk_save_insert_subkeys;
neal@3332
   666
    key_iter = pgp_tpk_key_iter(tpk);
neal@3332
   667
    pgp_key_t key;
neal@3332
   668
    while ((key = pgp_tpk_key_iter_next(key_iter, NULL, NULL))) {
neal@3332
   669
        pgp_keyid_t keyid = pgp_key_keyid(key);
neal@3332
   670
        char *keyid_hex = pgp_keyid_to_hex(keyid);
neal@3191
   671
        sqlite3_bind_text(stmt, 1, keyid_hex, -1, SQLITE_STATIC);
neal@3191
   672
        sqlite3_bind_text(stmt, 2, fpr, -1, SQLITE_STATIC);
neal@3191
   673
neal@3191
   674
        sqlite_result = sqlite3_step(stmt);
neal@3191
   675
        sqlite3_reset(stmt);
neal@3191
   676
        free(keyid_hex);
neal@3332
   677
        pgp_keyid_free(keyid);
neal@3191
   678
        if (sqlite_result != SQLITE_DONE) {
neal@3332
   679
            pgp_tpk_key_iter_free(key_iter);
neal@3332
   680
            ERROR_OUT(NULL, PEP_UNKNOWN_ERROR,
neal@3191
   681
                      "Updating subkeys: %s", sqlite3_errmsg(session->key_db));
neal@3191
   682
        }
neal@3191
   683
    }
neal@3332
   684
    pgp_tpk_key_iter_free(key_iter);
neal@3191
   685
    key_iter = NULL;
neal@3191
   686
us@3209
   687
    // Insert the "userids".
neal@3212
   688
    stmt = session->sq_sql.tpk_save_insert_userids;
neal@3332
   689
    user_id_iter = pgp_tpk_user_id_binding_iter(tpk);
neal@3332
   690
    pgp_user_id_binding_t binding;
us@3209
   691
    int first = 1;
neal@3332
   692
    while ((binding = pgp_user_id_binding_iter_next(user_id_iter))) {
neal@3332
   693
        char *user_id = pgp_user_id_binding_user_id(binding);
us@3209
   694
        if (!user_id || !*user_id)
us@3209
   695
            continue;
us@3209
   696
us@3209
   697
        // Ignore bindings with a self-revocation certificate, but no
us@3209
   698
        // self-signature.
neal@3332
   699
        if (!pgp_user_id_binding_selfsig(binding)) {
us@3209
   700
            free(user_id);
us@3209
   701
            continue;
us@3209
   702
        }
us@3209
   703
us@3209
   704
        char *name, *email;
us@3209
   705
        user_id_split(user_id, &name, &email); /* user_id is comsumed.  */
us@3209
   706
        // XXX: Correctly clean up name and email on error...
us@3209
   707
us@3209
   708
        if (email) {
us@3209
   709
            T("  userid: %s", email);
us@3209
   710
us@3209
   711
            sqlite3_bind_text(stmt, 1, email, -1, SQLITE_STATIC);
us@3209
   712
            sqlite3_bind_text(stmt, 2, fpr, -1, SQLITE_STATIC);
us@3209
   713
us@3209
   714
            sqlite_result = sqlite3_step(stmt);
us@3209
   715
            sqlite3_reset(stmt);
us@3209
   716
us@3209
   717
            if (sqlite_result != SQLITE_DONE) {
neal@3332
   718
                pgp_user_id_binding_iter_free(user_id_iter);
us@3209
   719
                free(name);
neal@3332
   720
                ERROR_OUT(NULL, PEP_UNKNOWN_ERROR,
us@3209
   721
                          "Updating userids: %s", sqlite3_errmsg(session->key_db));
us@3209
   722
            }
us@3209
   723
        }
us@3209
   724
us@3209
   725
        if (first && private_idents && is_tsk) {
us@3209
   726
            first = 0;
us@3209
   727
us@3209
   728
            // Create an identity for the primary user id.
us@3209
   729
            pEp_identity *ident = new_identity(email, fpr, NULL, name);
us@3209
   730
            if (ident == NULL)
neal@3332
   731
                ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "new_identity");
us@3209
   732
us@3209
   733
            *private_idents = identity_list_add(*private_idents, ident);
us@3209
   734
            if (*private_idents == NULL)
neal@3332
   735
                ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "identity_list_add");
us@3209
   736
        }
us@3209
   737
        free(email);
us@3209
   738
        free(name);
us@3209
   739
us@3209
   740
    }
neal@3332
   741
    pgp_user_id_binding_iter_free(user_id_iter);
us@3209
   742
    user_id_iter = NULL;
us@3209
   743
neal@3191
   744
 out:
neal@3191
   745
    // Prevent ERROR_OUT from causing an infinite loop.
neal@3191
   746
    if (! tried_commit) {
neal@3191
   747
        tried_commit = 1;
neal@3191
   748
        stmt = status == PEP_STATUS_OK
neal@3212
   749
            ? session->sq_sql.commit_transaction
neal@3212
   750
            : session->sq_sql.rollback_transaction;
neal@3191
   751
        int sqlite_result = sqlite3_step(stmt);
neal@3191
   752
        sqlite3_reset(stmt);
neal@3191
   753
        if (sqlite_result != SQLITE_DONE)
neal@3332
   754
            ERROR_OUT(NULL, PEP_UNKNOWN_ERROR,
neal@3191
   755
                      status == PEP_STATUS_OK
neal@3191
   756
                      ? "commit failed: %s" : "rollback failed: %s",
neal@3191
   757
                      sqlite3_errmsg(session->key_db));
neal@3191
   758
    }
neal@3191
   759
neal@3191
   760
    T("(%s) -> %s", fpr, pep_status_to_string(status));
neal@3191
   761
us@3209
   762
    if (user_id_iter)
neal@3332
   763
        pgp_user_id_binding_iter_free(user_id_iter);
neal@3191
   764
    if (key_iter)
neal@3332
   765
        pgp_tpk_key_iter_free(key_iter);
neal@3191
   766
    if (stmt)
neal@3191
   767
      sqlite3_reset(stmt);
neal@3191
   768
    free(tsk_buffer);
us@3209
   769
    if (tpk)
neal@3332
   770
        pgp_tpk_free(tpk);
neal@3191
   771
    free(fpr);
neal@3332
   772
    pgp_fingerprint_free(pgp_fpr);
neal@3191
   773
neal@3191
   774
    return status;
neal@3191
   775
}
neal@3191
   776
neal@3191
   777
struct decrypt_cookie {
neal@3191
   778
    PEP_SESSION session;
neal@3191
   779
    int get_secret_keys_called;
neal@3191
   780
    stringlist_t *recipient_keylist;
neal@3191
   781
    stringlist_t *signer_keylist;
neal@3191
   782
    int good_checksums;
neal@3191
   783
    int missing_keys;
neal@3191
   784
    int bad_checksums;
neal@3191
   785
    int decrypted;
neal@3191
   786
};
neal@3191
   787
neal@3332
   788
static pgp_status_t
neal@3191
   789
get_public_keys_cb(void *cookie_raw,
neal@3332
   790
                   pgp_keyid_t *keyids, size_t keyids_len,
neal@3332
   791
                   pgp_tpk_t **tpks, size_t *tpk_len,
neal@3191
   792
                   void (**our_free)(void *))
neal@3191
   793
{
neal@3191
   794
    struct decrypt_cookie *cookie = cookie_raw;
neal@3191
   795
    PEP_SESSION session = cookie->session;
neal@3191
   796
neal@3191
   797
    *tpks = calloc(keyids_len, sizeof(*tpks));
neal@3191
   798
    if (!*tpks)
neal@3332
   799
        return PGP_STATUS_UNKNOWN_ERROR;
neal@3191
   800
    *our_free = free;
neal@3191
   801
neal@3191
   802
    int i, j;
neal@3191
   803
    j = 0;
neal@3191
   804
    for (i = 0; i < keyids_len; i ++) {
neal@3332
   805
        pgp_tpk_t tpk = NULL;
neal@3332
   806
        pgp_status_t status
us@3209
   807
            = tpk_find_by_keyid(session, keyids[i], false, &tpk, NULL);
neal@3332
   808
        if (status == PGP_STATUS_SUCCESS)
neal@3191
   809
            (*tpks)[j ++] = tpk;
neal@3191
   810
    }
neal@3191
   811
    *tpk_len = j;
neal@3332
   812
    return PGP_STATUS_SUCCESS;
neal@3191
   813
}
neal@3191
   814
neal@3332
   815
static pgp_status_t
neal@3191
   816
get_secret_keys_cb(void *cookie_opaque,
neal@3332
   817
                   pgp_pkesk_t *pkesks, size_t pkesk_count,
neal@3332
   818
                   pgp_skesk_t *skesks, size_t skesk_count,
neal@3332
   819
                   pgp_secret_t *secret)
neal@3191
   820
{
neal@3332
   821
    pgp_error_t err = NULL;
neal@3191
   822
    struct decrypt_cookie *cookie = cookie_opaque;
neal@3191
   823
    PEP_SESSION session = cookie->session;
neal@3332
   824
    pgp_tpk_t *tsks = NULL;
neal@3191
   825
    int tsks_count = 0;
neal@3191
   826
    int wildcards = 0;
neal@3191
   827
neal@3191
   828
    if (cookie->get_secret_keys_called)
neal@3191
   829
        // Prevent iterations, which isn't needed since we don't
neal@3191
   830
        // support SKESKs.
neal@3332
   831
        return PGP_STATUS_UNKNOWN_ERROR;
neal@3191
   832
    cookie->get_secret_keys_called = 1;
neal@3191
   833
neal@3191
   834
    T("%zd PKESKs", pkesk_count);
neal@3191
   835
neal@3191
   836
    for (int i = 0; i < pkesk_count; i ++) {
neal@3332
   837
        pgp_pkesk_t pkesk = pkesks[i];
neal@3332
   838
        pgp_keyid_t keyid = pgp_pkesk_recipient(pkesk); /* Reference. */
neal@3332
   839
        char *keyid_str = pgp_keyid_to_hex(keyid);
neal@3332
   840
        pgp_tpk_key_iter_t key_iter = NULL;
neal@3191
   841
neal@3191
   842
        T("Considering PKESK for %s", keyid_str);
neal@3191
   843
neal@3191
   844
        if (strcmp(keyid_str, "0000000000000000") == 0) {
neal@3191
   845
            // Initially ignore wildcards.
neal@3191
   846
            wildcards = 1;
neal@3191
   847
            goto eol;
neal@3191
   848
        }
neal@3191
   849
neal@3191
   850
        // Collect the recipients.  Note: we must return the primary
neal@3191
   851
        // key's fingerprint.
neal@3332
   852
        pgp_tpk_t tpk = NULL;
us@3209
   853
        int is_tsk = 0;
us@3209
   854
        if (tpk_find_by_keyid(session, keyid, false, &tpk, &is_tsk) != PEP_STATUS_OK)
us@3209
   855
            goto eol;
us@3209
   856
neal@3332
   857
        pgp_fingerprint_t fp = pgp_tpk_fingerprint(tpk);
neal@3332
   858
        char *fp_string = pgp_fingerprint_to_hex(fp);
us@3209
   859
        stringlist_add_unique(cookie->recipient_keylist, fp_string);
us@3209
   860
        free(fp_string);
neal@3332
   861
        pgp_fingerprint_free(fp);
neal@3191
   862
neal@3191
   863
        if (cookie->decrypted)
neal@3191
   864
            goto eol;
neal@3191
   865
neal@3191
   866
        // See if we have the secret key.
neal@3332
   867
        assert(is_tsk == pgp_tpk_is_tsk(tpk));
us@3209
   868
        if (! is_tsk)
neal@3191
   869
            goto eol;
neal@3191
   870
neal@3332
   871
        key_iter = pgp_tpk_key_iter(tpk);
neal@3332
   872
        pgp_key_t key;
neal@3332
   873
        while ((key = pgp_tpk_key_iter_next(key_iter, NULL, NULL))) {
neal@3332
   874
            pgp_keyid_t this_keyid = pgp_key_keyid(key);
neal@3332
   875
            char *this_keyid_hex = pgp_keyid_to_hex(this_keyid);
neal@3332
   876
            pgp_keyid_free(this_keyid);
neal@3191
   877
neal@3191
   878
            int match = strcmp(keyid_str, this_keyid_hex) == 0;
neal@3191
   879
            free(this_keyid_hex);
neal@3191
   880
            if (match)
neal@3191
   881
                break;
neal@3191
   882
        }
neal@3191
   883
us@3209
   884
        if (key == NULL) {
neal@3191
   885
            assert(!"Inconsistent DB: key doesn't contain a subkey with keyid!");
us@3209
   886
            goto eol;
us@3209
   887
        }
neal@3191
   888
neal@3332
   889
        fprintf(stderr, "key: %s\n", pgp_key_debug(key));
neal@3332
   890
neal@3191
   891
        uint8_t algo;
neal@3191
   892
        uint8_t session_key[1024];
neal@3191
   893
        size_t session_key_len = sizeof(session_key);
neal@3332
   894
        if (pgp_pkesk_decrypt(&err, pkesk, key, &algo,
neal@3332
   895
                              session_key, &session_key_len) != 0) {
neal@3332
   896
            DUMP_ERR(err, PEP_UNKNOWN_ERROR, "pgp_pkesk_decrypt");
neal@3191
   897
            goto eol;
neal@3191
   898
        }
neal@3191
   899
neal@3191
   900
        T("Decrypted PKESK for %s", keyid_str);
neal@3191
   901
neal@3332
   902
        *secret = pgp_secret_cached(algo, session_key, session_key_len);
neal@3191
   903
        cookie->decrypted = 1;
neal@3191
   904
neal@3191
   905
    eol:
neal@3191
   906
        free(keyid_str);
neal@3191
   907
        if (key_iter)
neal@3332
   908
            pgp_tpk_key_iter_free(key_iter);
us@3209
   909
        if (tpk)
neal@3332
   910
            pgp_tpk_free(tpk);
neal@3191
   911
    }
neal@3191
   912
neal@3191
   913
    // Consider wildcard recipients.
neal@3191
   914
    if (wildcards) for (int i = 0; i < pkesk_count && !cookie->decrypted; i ++) {
neal@3332
   915
        pgp_pkesk_t pkesk = pkesks[i];
neal@3332
   916
        pgp_keyid_t keyid = pgp_pkesk_recipient(pkesk); /* Reference. */
neal@3332
   917
        char *keyid_str = pgp_keyid_to_hex(keyid);
neal@3332
   918
        pgp_tpk_key_iter_t key_iter = NULL;
neal@3191
   919
neal@3191
   920
        if (strcmp(keyid_str, "0000000000000000") != 0)
neal@3191
   921
            goto eol2;
neal@3191
   922
neal@3191
   923
        if (!tsks) {
us@3209
   924
            if (tpk_all(session, true, &tsks, &tsks_count) != PEP_STATUS_OK) {
neal@3332
   925
                DUMP_ERR(NULL, PEP_UNKNOWN_ERROR, "Getting all tsks");
neal@3191
   926
            }
neal@3191
   927
        }
neal@3191
   928
neal@3191
   929
        for (int j = 0; j < tsks_count; j ++) {
neal@3332
   930
            pgp_tpk_t tsk = tsks[j];
neal@3191
   931
neal@3332
   932
            key_iter = pgp_tpk_key_iter(tsk);
neal@3332
   933
            pgp_key_t key;
neal@3332
   934
            pgp_signature_t selfsig;
neal@3332
   935
            while ((key = pgp_tpk_key_iter_next(key_iter, &selfsig, NULL))) {
neal@3332
   936
                if (! (pgp_signature_can_encrypt_at_rest(selfsig)
neal@3332
   937
                       || pgp_signature_can_encrypt_for_transport(selfsig)))
neal@3191
   938
                    continue;
neal@3191
   939
neal@3332
   940
                fprintf(stderr, "key: %s\n", pgp_key_debug(key));
neal@3332
   941
neal@3191
   942
                // Note: for decryption to appear to succeed, we must
neal@3191
   943
                // get a valid algorithm (8 of 256 values) and a
neal@3191
   944
                // 16-bit checksum must match.  Thus, we have about a
neal@3191
   945
                // 1 in 2**21 chance of having a false positive here.
neal@3191
   946
                uint8_t algo;
neal@3191
   947
                uint8_t session_key[1024];
neal@3191
   948
                size_t session_key_len = sizeof(session_key);
neal@3332
   949
                if (pgp_pkesk_decrypt(&err, pkesk, key,
neal@3332
   950
                                      &algo, session_key, &session_key_len)) {
neal@3332
   951
                    pgp_error_free(err);
neal@3332
   952
                    err = NULL;
neal@3191
   953
                    continue;
neal@3332
   954
                }
neal@3191
   955
neal@3191
   956
                // Add it to the recipient list.
neal@3332
   957
                pgp_fingerprint_t fp = pgp_tpk_fingerprint(tsk);
neal@3332
   958
                char *fp_string = pgp_fingerprint_to_hex(fp);
neal@3191
   959
                T("wildcard recipient appears to be %s", fp_string);
neal@3191
   960
                stringlist_add_unique(cookie->recipient_keylist, fp_string);
neal@3191
   961
                free(fp_string);
neal@3332
   962
                pgp_fingerprint_free(fp);
neal@3191
   963
neal@3332
   964
                *secret = pgp_secret_cached(algo, session_key, session_key_len);
neal@3191
   965
                cookie->decrypted = 1;
neal@3191
   966
            }
neal@3191
   967
neal@3332
   968
            pgp_tpk_key_iter_free(key_iter);
neal@3191
   969
            key_iter = NULL;
neal@3191
   970
        }
neal@3191
   971
    eol2:
neal@3191
   972
        free(keyid_str);
neal@3191
   973
        if (key_iter)
neal@3332
   974
            pgp_tpk_key_iter_free(key_iter);
neal@3191
   975
    }
neal@3191
   976
neal@3191
   977
    if (tsks) {
neal@3191
   978
        for (int i = 0; i < tsks_count; i ++)
neal@3332
   979
            pgp_tpk_free(tsks[i]);
neal@3191
   980
        free(tsks);
neal@3191
   981
    }
neal@3191
   982
neal@3332
   983
    return cookie->decrypted ? PGP_STATUS_SUCCESS : PGP_STATUS_UNKNOWN_ERROR;
neal@3191
   984
}
neal@3191
   985
neal@3332
   986
static pgp_status_t
neal@3191
   987
check_signatures_cb(void *cookie_opaque,
neal@3332
   988
                   pgp_verification_results_t results, size_t levels)
neal@3191
   989
{
neal@3191
   990
    struct decrypt_cookie *cookie = cookie_opaque;
neal@3191
   991
    PEP_SESSION session = cookie->session;
neal@3191
   992
neal@3191
   993
    int level;
neal@3191
   994
    for (level = 0; level < levels; level ++) {
neal@3332
   995
        pgp_verification_result_t *vrs;
neal@3191
   996
        size_t vr_count;
neal@3332
   997
        pgp_verification_results_at_level(results, level, &vrs, &vr_count);
neal@3191
   998
neal@3191
   999
        int i;
neal@3191
  1000
        for (i = 0; i < vr_count; i ++) {
neal@3332
  1001
            pgp_tpk_t tpk = NULL;
neal@3332
  1002
            pgp_verification_result_code_t code
neal@3332
  1003
                = pgp_verification_result_code(vrs[i]);
neal@3191
  1004
neal@3332
  1005
            if (code == PGP_VERIFICATION_RESULT_CODE_BAD_CHECKSUM) {
neal@3191
  1006
                cookie->bad_checksums ++;
neal@3191
  1007
                continue;
neal@3191
  1008
            }
neal@3332
  1009
            if (code == PGP_VERIFICATION_RESULT_CODE_MISSING_KEY) {
neal@3191
  1010
                // No key, nothing we can do.
neal@3191
  1011
                cookie->missing_keys ++;
neal@3191
  1012
                continue;
neal@3191
  1013
            }
neal@3191
  1014
neal@3191
  1015
            // We need to add the fingerprint of the primary key to
neal@3191
  1016
            // cookie->signer_keylist.
neal@3332
  1017
            pgp_signature_t sig = pgp_verification_result_signature(vrs[i]);
neal@3191
  1018
neal@3191
  1019
            // First try looking up by the TPK using the
neal@3191
  1020
            // IssuerFingerprint subpacket.
neal@3332
  1021
            pgp_fingerprint_t issuer_fp = pgp_signature_issuer_fingerprint(sig);
neal@3191
  1022
            if (issuer_fp) {
neal@3332
  1023
                pgp_keyid_t issuer = pgp_fingerprint_to_keyid(issuer_fp);
us@3209
  1024
                if (tpk_find_by_keyid(session, issuer, false, &tpk, NULL) != PEP_STATUS_OK)
neal@3191
  1025
                    ; // Soft error.  Ignore.
neal@3332
  1026
                pgp_keyid_free(issuer);
neal@3332
  1027
                pgp_fingerprint_free(issuer_fp);
neal@3191
  1028
            }
neal@3191
  1029
neal@3191
  1030
            // If that is not available, try using the Issuer subpacket.
neal@3191
  1031
            if (!tpk) {
neal@3332
  1032
                pgp_keyid_t issuer = pgp_signature_issuer(sig);
neal@3191
  1033
                if (issuer) {
us@3209
  1034
                    if (tpk_find_by_keyid(session, issuer, false, &tpk, NULL) != PEP_STATUS_OK)
neal@3191
  1035
                        ; // Soft error.  Ignore.
neal@3191
  1036
                }
neal@3332
  1037
                pgp_keyid_free(issuer);
neal@3191
  1038
            }
neal@3191
  1039
neal@3191
  1040
            if (tpk) {
neal@3191
  1041
                // Ok, we have a TPK.
neal@3332
  1042
                pgp_fingerprint_t fp = pgp_tpk_fingerprint(tpk);
neal@3332
  1043
                char *fp_str = pgp_fingerprint_to_hex(fp);
neal@3191
  1044
                stringlist_add_unique(cookie->signer_keylist, fp_str);
neal@3191
  1045
neal@3191
  1046
                // XXX: Check that the TPK and the key used to make
neal@3191
  1047
                // the signature and the signature itself are alive
neal@3191
  1048
                // and not revoked.  Revoked =>
neal@3191
  1049
                // PEP_DECRYPT_SIGNATURE_DOES_NOT_MATCH; Expired key
neal@3191
  1050
                // or sig => PEP_DECRYPTED.
neal@3191
  1051
                cookie->good_checksums ++;
neal@3191
  1052
neal@3191
  1053
                free(fp_str);
neal@3332
  1054
                pgp_fingerprint_free(fp);
neal@3332
  1055
                pgp_tpk_free(tpk);
neal@3191
  1056
            } else {
neal@3191
  1057
                // If we get
neal@3332
  1058
                // PGP_VERIFICATION_RESULT_CODE_GOOD_CHECKSUM, then the
neal@3191
  1059
                // TPK should be available.  But, another process
neal@3191
  1060
                // could have deleted the key from the store in the
neal@3191
  1061
                // mean time, so be tolerant.
neal@3191
  1062
                cookie->missing_keys ++;
neal@3191
  1063
            }
neal@3191
  1064
        }
neal@3191
  1065
    }
neal@3191
  1066
neal@3332
  1067
    return PGP_STATUS_SUCCESS;
neal@3191
  1068
}
neal@3191
  1069
neal@3191
  1070
PEP_STATUS pgp_decrypt_and_verify(
neal@3191
  1071
    PEP_SESSION session, const char *ctext, size_t csize,
neal@3191
  1072
    const char *dsigtext, size_t dsigsize,
neal@3191
  1073
    char **ptext, size_t *psize, stringlist_t **keylist,
neal@3191
  1074
    char** filename_ptr)
neal@3191
  1075
{
neal@3191
  1076
    PEP_STATUS status = PEP_STATUS_OK;
neal@3191
  1077
    struct decrypt_cookie cookie = { session, 0, NULL, NULL, 0, 0, 0, };
neal@3332
  1078
    pgp_reader_t reader = NULL;
neal@3332
  1079
    pgp_writer_t writer = NULL;
neal@3191
  1080
    *ptext = NULL;
neal@3191
  1081
    *psize = 0;
neal@3191
  1082
neal@3191
  1083
    // XXX: We don't yet handle detached signatures over encrypted
neal@3191
  1084
    // messages.
neal@3191
  1085
    assert(!dsigtext);
neal@3191
  1086
neal@3191
  1087
    cookie.recipient_keylist = new_stringlist(NULL);
neal@3191
  1088
    if (!cookie.recipient_keylist)
neal@3332
  1089
        ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "recipient_keylist");
neal@3191
  1090
neal@3191
  1091
    cookie.signer_keylist = new_stringlist(NULL);
neal@3191
  1092
    if (!cookie.signer_keylist)
neal@3332
  1093
        ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "signer_keylist");
neal@3191
  1094
neal@3332
  1095
    reader = pgp_reader_from_bytes((const uint8_t *) ctext, csize);
neal@3191
  1096
    if (! reader)
neal@3332
  1097
        ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "Creating reader");
neal@3191
  1098
neal@3332
  1099
    writer = pgp_writer_alloc((void **) ptext, psize);
neal@3191
  1100
    if (! writer)
neal@3332
  1101
        ERROR_OUT(NULL, PEP_UNKNOWN_ERROR, "Creating writer");
neal@3191
  1102
neal@3332
  1103
    pgp_error_t err = NULL;
neal@3332
  1104
    pgp_status_t pgp_status
neal@3332
  1105
        = pgp_decrypt(&err, reader, writer,
neal@3332
  1106
                      get_public_keys_cb, get_secret_keys_cb,
neal@3332
  1107
                      check_signatures_cb, &cookie);
neal@3332
  1108
    if (pgp_status)
neal@3332
  1109
        ERROR_OUT(err, PEP_DECRYPT_NO_KEY, "pgp_decrypt");
neal@3191
  1110
neal@3191
  1111
    if (! cookie.decrypted)
neal@3332
  1112
        ERROR_OUT(err, PEP_DECRYPT_NO_KEY, "Decryption failed");
neal@3191
  1113
neal@3191
  1114
    // Add a terminating NUL for naive users
neal@3191
  1115
    void *t = realloc(*ptext, *psize + 1);
neal@3191
  1116
    if (! t)
neal@3332
  1117
        ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "out of memory");
neal@3191
  1118
    *ptext = t;
neal@3191
  1119
    (*ptext)[*psize] = 0;
neal@3191
  1120
neal@3191
  1121
    if (! cookie.signer_keylist) {
neal@3191
  1122
        cookie.signer_keylist = new_stringlist("");
neal@3191
  1123
        if (! cookie.signer_keylist)
neal@3332
  1124
            ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "cookie.signer_keylist");
neal@3191
  1125
    }
neal@3191
  1126
    if (!cookie.signer_keylist->value)
neal@3191
  1127
        stringlist_add(cookie.signer_keylist, "");
neal@3191
  1128
neal@3191
  1129
    *keylist = cookie.signer_keylist;
neal@3191
  1130
    stringlist_append(*keylist, cookie.recipient_keylist);
neal@3191
  1131
neal@3191
  1132
 out:
neal@3191
  1133
    if (status == PEP_STATUS_OK) {
neal@3191
  1134
        if (cookie.bad_checksums) {
neal@3191
  1135
            // If there are any bad signatures, fail.
neal@3191
  1136
            status = PEP_DECRYPT_SIGNATURE_DOES_NOT_MATCH;
neal@3191
  1137
        } else if (cookie.good_checksums) {
neal@3191
  1138
            // If there is at least one signature that we can verify,
neal@3191
  1139
            // succeed.
neal@3191
  1140
            status = PEP_DECRYPTED_AND_VERIFIED;
neal@3191
  1141
        } else {
neal@3191
  1142
            // We couldn't verify any signatures (possibly because we
neal@3191
  1143
            // don't have the keys).
neal@3191
  1144
            status = PEP_DECRYPTED;
neal@3191
  1145
        }
neal@3191
  1146
    } else {
neal@3191
  1147
        free_stringlist(cookie.recipient_keylist);
neal@3191
  1148
        free_stringlist(cookie.signer_keylist);
neal@3191
  1149
        free(*ptext);
neal@3191
  1150
    }
neal@3191
  1151
neal@3191
  1152
    if (reader)
neal@3332
  1153
        pgp_reader_free(reader);
neal@3191
  1154
    if (writer)
neal@3332
  1155
        pgp_writer_free(writer);
neal@3191
  1156
neal@3191
  1157
    T("-> %s", pep_status_to_string(status));
neal@3191
  1158
    return status;
neal@3191
  1159
}
neal@3191
  1160
neal@3191
  1161
PEP_STATUS pgp_verify_text(
neal@3191
  1162
    PEP_SESSION session, const char *text, size_t size,
neal@3191
  1163
    const char *signature, size_t sig_size, stringlist_t **keylist)
neal@3191
  1164
{
neal@3191
  1165
    PEP_STATUS status = PEP_STATUS_OK;
neal@3332
  1166
    pgp_error_t err = NULL;
neal@3191
  1167
    struct decrypt_cookie cookie = { session, 0, NULL, NULL, 0, 0, 0, };
neal@3332
  1168
    pgp_reader_t reader = NULL;
neal@3332
  1169
    pgp_reader_t dsig_reader = NULL;
neal@3191
  1170
neal@3191
  1171
    if (size == 0 || sig_size == 0)
neal@3191
  1172
        return PEP_DECRYPT_WRONG_FORMAT;
neal@3191
  1173
neal@3191
  1174
    cookie.recipient_keylist = new_stringlist(NULL);
neal@3191
  1175
    if (!cookie.recipient_keylist)
neal@3332
  1176
        ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "out of memory");
neal@3191
  1177
neal@3191
  1178
    cookie.signer_keylist = new_stringlist(NULL);
neal@3191
  1179
    if (!cookie.signer_keylist)
neal@3332
  1180
        ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "out of memory");
neal@3191
  1181
neal@3332
  1182
    reader = pgp_reader_from_bytes((const uint8_t *) text, size);
neal@3191
  1183
    if (! reader)
neal@3332
  1184
        ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "Creating reader");
neal@3191
  1185
neal@3191
  1186
    dsig_reader = NULL;
neal@3191
  1187
    if (signature) {
neal@3332
  1188
        dsig_reader = pgp_reader_from_bytes((uint8_t *) signature, sig_size);
neal@3191
  1189
        if (! dsig_reader)
neal@3332
  1190
            ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "Creating signature reader");
neal@3191
  1191
    }
neal@3191
  1192
neal@3332
  1193
    if (pgp_verify(&err, reader, dsig_reader, /* output */ NULL,
neal@3191
  1194
                  get_public_keys_cb, check_signatures_cb, &cookie))
neal@3332
  1195
        ERROR_OUT(err, PEP_UNKNOWN_ERROR, "pgp_verify");
neal@3191
  1196
neal@3191
  1197
    if (! cookie.signer_keylist) {
neal@3191
  1198
        cookie.signer_keylist = new_stringlist("");
neal@3191
  1199
        if (! cookie.signer_keylist)
neal@3332
  1200
            ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "cookie.signer_keylist");
neal@3191
  1201
    }
neal@3191
  1202
    if (!cookie.signer_keylist->value)
neal@3191
  1203
        stringlist_add(cookie.signer_keylist, "");
neal@3191
  1204
neal@3191
  1205
    *keylist = cookie.signer_keylist;
neal@3191
  1206
    stringlist_append(*keylist, cookie.recipient_keylist);
neal@3191
  1207
neal@3191
  1208
 out:
neal@3191
  1209
    if (status == PEP_STATUS_OK) {
neal@3191
  1210
        if (cookie.bad_checksums) {
neal@3191
  1211
            // If there are any bad signatures, fail.
neal@3191
  1212
            status = PEP_DECRYPT_SIGNATURE_DOES_NOT_MATCH;
neal@3191
  1213
        } else if (cookie.good_checksums) {
neal@3191
  1214
            // If there is at least one signature that we can verify,
neal@3191
  1215
            // succeed.
neal@3191
  1216
            status = PEP_VERIFIED;
neal@3191
  1217
        } else {
neal@3191
  1218
            // We couldn't verify any signatures (possibly because we
neal@3191
  1219
            // don't have the keys).
neal@3191
  1220
            status = PEP_UNENCRYPTED;
neal@3191
  1221
        }
neal@3191
  1222
    } else {
neal@3191
  1223
        free_stringlist(cookie.recipient_keylist);
neal@3191
  1224
        free_stringlist(cookie.signer_keylist);
neal@3191
  1225
    }
neal@3191
  1226
neal@3191
  1227
    if (reader)
neal@3332
  1228
        pgp_reader_free(reader);
neal@3191
  1229
    if (dsig_reader)
neal@3332
  1230
        pgp_reader_free(dsig_reader);
neal@3191
  1231
neal@3191
  1232
    T("-> %s", pep_status_to_string(status));
neal@3191
  1233
    return status;
neal@3191
  1234
}
neal@3191
  1235
neal@3191
  1236
neal@3191
  1237
PEP_STATUS pgp_sign_only(
neal@3191
  1238
    PEP_SESSION session, const char* fpr, const char *ptext,
neal@3191
  1239
    size_t psize, char **stext, size_t *ssize)
neal@3191
  1240
{
neal@3191
  1241
    assert(session);
neal@3191
  1242
    assert(fpr && fpr[0]);
neal@3191
  1243
    assert(ptext);
neal@3191
  1244
    assert(psize);
neal@3191
  1245
    assert(stext);
neal@3191
  1246
    assert(ssize);
neal@3191
  1247
neal@3191
  1248
    PEP_STATUS status = PEP_STATUS_OK;
neal@3332
  1249
    pgp_error_t err = NULL;
neal@3332
  1250
    pgp_tpk_t signer = NULL;
neal@3332
  1251
    pgp_writer_stack_t ws = NULL;
neal@3191
  1252
us@3209
  1253
    status = tpk_find_by_fpr_hex(session, fpr, true, &signer, NULL);
neal@3332
  1254
    ERROR_OUT(NULL, status, "Looking up key '%s'", fpr);
neal@3191
  1255
neal@3332
  1256
    pgp_writer_t writer = pgp_writer_alloc((void **) stext, ssize);
neal@3332
  1257
    writer = pgp_armor_writer_new(&err, writer,
neal@3332
  1258
                                  PGP_ARMOR_KIND_MESSAGE, NULL, 0);
neal@3191
  1259
    if (!writer)
neal@3332
  1260
        ERROR_OUT(err, PEP_UNKNOWN_ERROR, "Setting up armor writer");
neal@3191
  1261
neal@3332
  1262
    ws = pgp_writer_stack_message(writer);
neal@3191
  1263
neal@3332
  1264
    ws = pgp_signer_new_detached(&err, ws, &signer, 1);
neal@3191
  1265
    if (!ws)
neal@3332
  1266
        ERROR_OUT(err, PEP_UNKNOWN_ERROR, "Setting up signer");
neal@3191
  1267
neal@3332
  1268
    pgp_status_t write_status =
neal@3332
  1269
        pgp_writer_stack_write_all (&err, ws,
neal@3332
  1270
                                    (uint8_t *) ptext, psize);
neal@3191
  1271
    if (write_status != 0)
neal@3332
  1272
        ERROR_OUT(err, PEP_UNKNOWN_ERROR, "Encrypting message");
neal@3191
  1273
neal@3191
  1274
    // Add a terminating NUL for naive users
neal@3191
  1275
    void *t = realloc(*stext, *ssize + 1);
neal@3191
  1276
    if (! t)
neal@3332
  1277
        ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "out of memory");
neal@3191
  1278
    *stext = t;
neal@3191
  1279
    (*stext)[*ssize] = 0;
neal@3191
  1280
neal@3191
  1281
 out:
neal@3191
  1282
    if (ws) {
neal@3332
  1283
        pgp_status_t pgp_status = pgp_writer_stack_finalize (&err, ws);
neal@3191
  1284
        ws = NULL;
neal@3332
  1285
        if (pgp_status != 0)
neal@3332
  1286
            ERROR_OUT(err, PEP_UNKNOWN_ERROR, "Flushing writer");
neal@3191
  1287
    }
neal@3191
  1288
neal@3191
  1289
    if (signer)
neal@3332
  1290
        pgp_tpk_free(signer);
neal@3191
  1291
neal@3191
  1292
    T("(%s)-> %s", fpr, pep_status_to_string(status));
neal@3191
  1293
    return status;
neal@3191
  1294
}
neal@3191
  1295
neal@3191
  1296
static PEP_STATUS pgp_encrypt_sign_optional(
neal@3191
  1297
    PEP_SESSION session, const stringlist_t *keylist, const char *ptext,
neal@3191
  1298
    size_t psize, char **ctext, size_t *csize, bool sign)
neal@3191
  1299
{
neal@3191
  1300
    PEP_STATUS status = PEP_STATUS_OK;
neal@3332
  1301
    pgp_error_t err = NULL;
neal@3191
  1302
    int keys_count = 0;
neal@3332
  1303
    pgp_tpk_t *keys = NULL;
neal@3332
  1304
    pgp_tpk_t signer = NULL;
neal@3332
  1305
    pgp_writer_stack_t ws = NULL;
neal@3191
  1306
neal@3191
  1307
    assert(session);
neal@3191
  1308
    assert(keylist);
neal@3191
  1309
    assert(ptext);
neal@3191
  1310
    assert(psize);
neal@3191
  1311
    assert(ctext);
neal@3191
  1312
    assert(csize);
neal@3191
  1313
neal@3191
  1314
    *ctext = NULL;
neal@3191
  1315
    *csize = 0;
neal@3191
  1316
neal@3191
  1317
    keys = calloc(stringlist_length(keylist), sizeof(*keys));
neal@3191
  1318
    if (keys == NULL)
neal@3332
  1319
        ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "out of memory");
neal@3191
  1320
neal@3191
  1321
    // Get the keys for the recipients.
neal@3191
  1322
    const stringlist_t *_keylist;
neal@3191
  1323
    for (_keylist = keylist; _keylist != NULL; _keylist = _keylist->next) {
neal@3191
  1324
        assert(_keylist->value);
neal@3332
  1325
        pgp_fingerprint_t pgp_fpr = pgp_fingerprint_from_hex(_keylist->value);
neal@3332
  1326
        status = tpk_find_by_fpr(session, pgp_fpr, false, &keys[keys_count ++], NULL);
neal@3332
  1327
        pgp_fingerprint_free(pgp_fpr);
neal@3332
  1328
        ERROR_OUT(NULL, status, "Looking up key for recipient '%s'", _keylist->value);
neal@3191
  1329
    }
neal@3191
  1330
neal@3191
  1331
    if (sign) {
neal@3191
  1332
        // The first key in the keylist is the signer.
us@3209
  1333
        status = tpk_find_by_fpr_hex(session, keylist->value, true, &signer, NULL);
neal@3332
  1334
        ERROR_OUT(NULL, status, "Looking up key for signing '%s'", keylist->value);
neal@3191
  1335
    }
neal@3191
  1336
neal@3332
  1337
    pgp_writer_t writer = pgp_writer_alloc((void **) ctext, csize);
neal@3332
  1338
    writer = pgp_armor_writer_new(&err, writer,
neal@3332
  1339
                                  PGP_ARMOR_KIND_MESSAGE, NULL, 0);
neal@3191
  1340
    if (!writer)
neal@3332
  1341
        ERROR_OUT(err, PEP_UNKNOWN_ERROR, "Setting up armor writer");
neal@3191
  1342
neal@3332
  1343
    ws = pgp_writer_stack_message(writer);
neal@3332
  1344
    ws = pgp_encryptor_new (&err, ws,
neal@3191
  1345
                           NULL, 0, keys, keys_count,
neal@3332
  1346
                           PGP_ENCRYPTION_MODE_FOR_TRANSPORT);
neal@3191
  1347
    if (!ws) {
neal@3332
  1348
        pgp_writer_free(writer);
neal@3332
  1349
        ERROR_OUT(err, PEP_UNKNOWN_ERROR, "Setting up encryptor");
neal@3191
  1350
    }
neal@3191
  1351
neal@3191
  1352
    if (sign) {
neal@3332
  1353
        pgp_key_t primary_key = pgp_tpk_primary (signer);
neal@3332
  1354
        pgp_key_pair_t primary_keypair
neal@3332
  1355
            = pgp_key_into_key_pair (NULL, pgp_key_clone (primary_key));
neal@3332
  1356
        pgp_key_free (primary_key);
neal@3332
  1357
        assert (primary_keypair);
neal@3332
  1358
        pgp_signer_t primary_signer = pgp_key_pair_as_signer (primary_keypair);
neal@3332
  1359
neal@3332
  1360
        ws = pgp_signer_new(&err, ws, &signer, 1);
neal@3191
  1361
        if (!ws)
neal@3332
  1362
            ERROR_OUT(err, PEP_UNKNOWN_ERROR, "Setting up signer");
neal@3191
  1363
    }
neal@3191
  1364
neal@3332
  1365
    ws = pgp_literal_writer_new (&err, ws);
neal@3191
  1366
    if (!ws)
neal@3332
  1367
        ERROR_OUT(err, PEP_UNKNOWN_ERROR, "Setting up literal writer");
neal@3191
  1368
neal@3332
  1369
    pgp_status_t write_status =
neal@3332
  1370
        pgp_writer_stack_write_all (&err, ws,
neal@3332
  1371
                                    (uint8_t *) ptext, psize);
neal@3191
  1372
    if (write_status != 0)
neal@3332
  1373
        ERROR_OUT(err, PEP_UNKNOWN_ERROR, "Encrypting message");
neal@3191
  1374
neal@3191
  1375
    // Add a terminating NUL for naive users
neal@3191
  1376
    void *t = realloc(*ctext, *csize + 1);
neal@3191
  1377
    if (! t)
neal@3332
  1378
        ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "out of memory");
neal@3191
  1379
    *ctext = t;
neal@3191
  1380
    (*ctext)[*csize] = 0;
neal@3191
  1381
neal@3191
  1382
 out:
neal@3191
  1383
    if (ws) {
neal@3332
  1384
        pgp_status_t pgp_status = pgp_writer_stack_finalize (&err, ws);
neal@3191
  1385
        ws = NULL;
neal@3332
  1386
        if (pgp_status != 0)
neal@3332
  1387
            ERROR_OUT(err, PEP_UNKNOWN_ERROR, "Flushing writer");
neal@3191
  1388
    }
neal@3191
  1389
neal@3191
  1390
    if (signer)
neal@3332
  1391
        pgp_tpk_free(signer);
neal@3191
  1392
    for (int i = 0; i < keys_count; i ++)
neal@3332
  1393
        pgp_tpk_free(keys[i]);
neal@3191
  1394
    free(keys);
neal@3191
  1395
neal@3191
  1396
    T("-> %s", pep_status_to_string(status));
neal@3191
  1397
    return status;
neal@3191
  1398
}
neal@3191
  1399
neal@3191
  1400
PEP_STATUS pgp_encrypt_only(
neal@3191
  1401
    PEP_SESSION session, const stringlist_t *keylist, const char *ptext,
neal@3191
  1402
    size_t psize, char **ctext, size_t *csize)
neal@3191
  1403
{
neal@3191
  1404
    return pgp_encrypt_sign_optional(session, keylist, ptext,
neal@3191
  1405
        psize, ctext, csize, false);
neal@3191
  1406
}
neal@3191
  1407
neal@3191
  1408
PEP_STATUS pgp_encrypt_and_sign(
neal@3191
  1409
    PEP_SESSION session, const stringlist_t *keylist, const char *ptext,
neal@3191
  1410
    size_t psize, char **ctext, size_t *csize)
neal@3191
  1411
{
neal@3191
  1412
    return pgp_encrypt_sign_optional(session, keylist, ptext,
neal@3191
  1413
        psize, ctext, csize, true);
neal@3191
  1414
}
neal@3191
  1415
neal@3191
  1416
neal@3191
  1417
PEP_STATUS pgp_generate_keypair(PEP_SESSION session, pEp_identity *identity)
neal@3191
  1418
{
neal@3191
  1419
    PEP_STATUS status = PEP_STATUS_OK;
neal@3332
  1420
    pgp_error_t err = NULL;
neal@3191
  1421
    char *userid = NULL;
neal@3332
  1422
    pgp_tpk_t tpk = NULL;
neal@3332
  1423
    pgp_fingerprint_t pgp_fpr = NULL;
neal@3191
  1424
    char *fpr = NULL;
neal@3191
  1425
neal@3191
  1426
    assert(session);
neal@3191
  1427
    assert(identity);
neal@3191
  1428
    assert(identity->address);
neal@3191
  1429
    assert(identity->fpr == NULL || identity->fpr[0] == 0);
neal@3191
  1430
    assert(identity->username);
neal@3191
  1431
neal@3191
  1432
    asprintf(&userid, "%s <%s>", identity->username, identity->address);
neal@3191
  1433
    if (! userid)
neal@3332
  1434
        ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "asprintf");
neal@3191
  1435
us@3209
  1436
    T("(%s)", userid);
us@3209
  1437
neal@3191
  1438
    // Generate a key.
neal@3332
  1439
    pgp_tsk_t tsk;
neal@3332
  1440
    pgp_signature_t rev;
neal@3332
  1441
    if (pgp_tsk_new(&err, userid, &tsk, &rev) != 0)
neal@3332
  1442
        ERROR_OUT(err, PEP_CANNOT_CREATE_KEY, "Generating a key pair");
neal@3191
  1443
neal@3191
  1444
    // XXX: We should return this.
neal@3332
  1445
    // pgp_signature_free(rev);
neal@3191
  1446
neal@3332
  1447
    tpk = pgp_tsk_into_tpk(tsk);
neal@3191
  1448
neal@3191
  1449
    // Get the fingerprint.
neal@3332
  1450
    pgp_fpr = pgp_tpk_fingerprint(tpk);
neal@3332
  1451
    fpr = pgp_fingerprint_to_hex(pgp_fpr);
neal@3191
  1452
us@3209
  1453
    status = tpk_save(session, tpk, NULL);
neal@3191
  1454
    tpk = NULL;
neal@3191
  1455
    if (status != 0)
neal@3332
  1456
        ERROR_OUT(NULL, PEP_CANNOT_CREATE_KEY, "saving TSK");
neal@3191
  1457
neal@3191
  1458
    free(identity->fpr);
neal@3191
  1459
    identity->fpr = fpr;
neal@3191
  1460
    fpr = NULL;
neal@3191
  1461
neal@3191
  1462
 out:
neal@3332
  1463
    if (pgp_fpr)
neal@3332
  1464
        pgp_fingerprint_free(pgp_fpr);
neal@3191
  1465
    free(fpr);
neal@3191
  1466
    if (tpk)
neal@3332
  1467
        pgp_tpk_free(tpk);
neal@3191
  1468
    free(userid);
neal@3191
  1469
neal@3191
  1470
    T("-> %s", pep_status_to_string(status));
neal@3191
  1471
    return status;
neal@3191
  1472
}
neal@3191
  1473
neal@3191
  1474
PEP_STATUS pgp_delete_keypair(PEP_SESSION session, const char *fpr_raw)
neal@3191
  1475
{
neal@3191
  1476
    PEP_STATUS status = PEP_STATUS_OK;
neal@3332
  1477
    char *fpr = pgp_fingerprint_canonicalize(fpr_raw);
neal@3191
  1478
neal@3191
  1479
    T("(%s)", fpr);
neal@3191
  1480
neal@3191
  1481
    // XXX: Can also be used for deleting public keys!!!
neal@3191
  1482
    assert(!"implement me");
neal@3191
  1483
neal@3191
  1484
    T("(%s) -> %s", fpr, pep_status_to_string(status));
neal@3191
  1485
neal@3191
  1486
    free(fpr);
neal@3191
  1487
    return status;
neal@3191
  1488
}
neal@3191
  1489
neal@3191
  1490
// XXX: This needs to handle not only TPKs, but also keyrings and
neal@3191
  1491
// revocation certificates.  Right now, we only import a single TPK
neal@3191
  1492
// and ignore everything else.
neal@3191
  1493
PEP_STATUS pgp_import_keydata(PEP_SESSION session, const char *key_data,
neal@3191
  1494
                              size_t size, identity_list **private_idents)
neal@3191
  1495
{
neal@3191
  1496
    PEP_STATUS status = PEP_STATUS_OK;
neal@3332
  1497
    pgp_error_t err;
neal@3191
  1498
neal@3191
  1499
    if (private_idents)
neal@3191
  1500
        *private_idents = NULL;
neal@3191
  1501
neal@3191
  1502
    T("parsing %zd bytes", size);
neal@3191
  1503
neal@3332
  1504
    pgp_packet_parser_result_t ppr
neal@3332
  1505
        = pgp_packet_parser_from_bytes(&err, (uint8_t *) key_data, size);
neal@3191
  1506
    if (! ppr)
neal@3332
  1507
        ERROR_OUT(err, PEP_UNKNOWN_ERROR, "Creating packet parser");
neal@3191
  1508
neal@3332
  1509
    pgp_tag_t tag = pgp_packet_parser_result_tag(ppr);
neal@3191
  1510
    switch (tag) {
neal@3332
  1511
    case PGP_TAG_SIGNATURE:
neal@3191
  1512
        // XXX: Implement me.
neal@3191
  1513
        assert(!"Have possible revocation certificate!");
neal@3191
  1514
        break;
neal@3191
  1515
neal@3332
  1516
    case PGP_TAG_PUBLIC_KEY:
neal@3332
  1517
    case PGP_TAG_SECRET_KEY: {
neal@3332
  1518
        pgp_tpk_t tpk = pgp_tpk_from_packet_parser(&err, ppr);
neal@3191
  1519
        if (! tpk)
neal@3332
  1520
            ERROR_OUT(err, PEP_UNKNOWN_ERROR, "parsing key data");
neal@3191
  1521
neal@3191
  1522
        // If private_idents is not NULL and there is any private key
neal@3191
  1523
        // material, it will be saved.
us@3209
  1524
        status = tpk_save(session, tpk, private_idents);
krista@3272
  1525
        if (status == PEP_STATUS_OK)
krista@3272
  1526
            status = PEP_KEY_IMPORTED;
neal@3332
  1527
        ERROR_OUT(NULL, status, "saving TPK");
neal@3191
  1528
        break;
neal@3191
  1529
    }
neal@3191
  1530
    default:
neal@3332
  1531
        ERROR_OUT(NULL, PEP_NO_KEY_IMPORTED,
neal@3332
  1532
                  "Can't import %s", pgp_tag_to_string(tag));
neal@3191
  1533
        break;
neal@3191
  1534
    }
neal@3191
  1535
neal@3191
  1536
 out:
neal@3191
  1537
    T("-> %s", pep_status_to_string(status));
neal@3191
  1538
    return status;
neal@3191
  1539
}
neal@3191
  1540
neal@3191
  1541
PEP_STATUS pgp_export_keydata(
neal@3191
  1542
        PEP_SESSION session, const char *fpr, char **key_data, size_t *size,
neal@3191
  1543
        bool secret)
neal@3191
  1544
{
neal@3191
  1545
    PEP_STATUS status = PEP_STATUS_OK;
neal@3332
  1546
    pgp_error_t err = NULL;
neal@3332
  1547
    pgp_tpk_t secret_key = NULL;
neal@3332
  1548
    pgp_tpk_t tpk = NULL;
neal@3191
  1549
neal@3191
  1550
    assert(session);
neal@3191
  1551
    assert(fpr);
neal@3191
  1552
    assert(key_data);
neal@3191
  1553
    assert(*key_data == NULL);
neal@3191
  1554
    assert(size);
neal@3191
  1555
neal@3191
  1556
    *size = 0;
neal@3191
  1557
neal@3191
  1558
    T("(%s, %s)", fpr, secret ? "secret" : "public");
neal@3191
  1559
neal@3191
  1560
    if (secret) {
us@3209
  1561
        status = tpk_find_by_fpr_hex(session, fpr, true, &secret_key, NULL);
us@3209
  1562
        if (status == PEP_KEY_NOT_FOUND)
neal@3191
  1563
            status = PEP_STATUS_OK;
neal@3332
  1564
        ERROR_OUT(NULL, status, "Looking up TSK for %s", fpr);
neal@3191
  1565
    }
neal@3191
  1566
neal@3332
  1567
    pgp_fingerprint_t pgp_fpr = pgp_fingerprint_from_hex(fpr);
neal@3332
  1568
    status = tpk_find_by_fpr(session, pgp_fpr, false, &tpk, NULL);
neal@3332
  1569
    pgp_fingerprint_free(pgp_fpr);
neal@3332
  1570
    ERROR_OUT(NULL, status, "Looking up TPK for %s", fpr);
neal@3191
  1571
neal@3191
  1572
    if (secret_key) {
neal@3332
  1573
        tpk = pgp_tpk_merge(&err, tpk, secret_key);
neal@3332
  1574
        // pgp_tpk_merge can return NULL if the primary keys don't
neal@3191
  1575
        // match.  But, we looked up the tpk by the secret key's
neal@3191
  1576
        // fingerprint so this should not be possible.
neal@3191
  1577
        assert(tpk);
neal@3332
  1578
        if (! tpk)
neal@3332
  1579
            ERROR_OUT(err, PEP_UNKNOWN_ERROR, "merging TPKs");
neal@3191
  1580
        secret_key = NULL;
neal@3191
  1581
    }
neal@3191
  1582
neal@3332
  1583
    pgp_writer_t memory_writer = pgp_writer_alloc((void **) key_data, size);
neal@3191
  1584
    if (! memory_writer)
neal@3332
  1585
        ERROR_OUT(NULL, PEP_UNKNOWN_ERROR, "creating memory writer");
neal@3332
  1586
    pgp_writer_t armor_writer = pgp_armor_writer_new(&err,
neal@3332
  1587
                                                     memory_writer,
neal@3332
  1588
                                                     PGP_ARMOR_KIND_PUBLICKEY,
neal@3332
  1589
                                                     NULL, 0);
neal@3191
  1590
    if (! armor_writer) {
neal@3332
  1591
        pgp_writer_free(memory_writer);
neal@3332
  1592
        ERROR_OUT(err, PEP_UNKNOWN_ERROR, "creating armored writer");
neal@3191
  1593
    }
neal@3191
  1594
neal@3191
  1595
    if (secret) {
neal@3332
  1596
        pgp_tsk_t tsk = pgp_tpk_into_tsk(tpk);
neal@3332
  1597
        if (pgp_tsk_serialize(&err, tsk, armor_writer))
neal@3332
  1598
            ERROR_OUT(err, PEP_UNKNOWN_ERROR, "serializing TSK");
neal@3332
  1599
        tpk = pgp_tsk_into_tpk(tsk);
neal@3191
  1600
    } else {
neal@3332
  1601
        if (pgp_tpk_serialize(&err, tpk, armor_writer))
neal@3332
  1602
            ERROR_OUT(err, PEP_UNKNOWN_ERROR, "serializing TPK");
neal@3191
  1603
    }
neal@3191
  1604
neal@3191
  1605
 out:
neal@3191
  1606
    if (tpk)
neal@3332
  1607
        pgp_tpk_free(tpk);
neal@3191
  1608
neal@3191
  1609
    if (armor_writer)
neal@3332
  1610
        pgp_writer_free(armor_writer);
neal@3191
  1611
neal@3191
  1612
    if (secret_key)
neal@3332
  1613
        pgp_tpk_free(secret_key);
neal@3191
  1614
neal@3191
  1615
    T("(%s) -> %s", fpr, pep_status_to_string(status));
neal@3191
  1616
    return status;
neal@3191
  1617
}
neal@3191
  1618
neal@3191
  1619
char* _undot_address(const char* address) {
neal@3191
  1620
    if (!address)
neal@3191
  1621
        return NULL;
neal@3191
  1622
neal@3191
  1623
    int addr_len = strlen(address);
neal@3191
  1624
    const char* at = strstr(address, "@");
neal@3191
  1625
neal@3191
  1626
    if (!at)
neal@3191
  1627
        at = address + addr_len;
neal@3191
  1628
neal@3191
  1629
    char* retval = calloc(1, addr_len + 1);
neal@3191
  1630
neal@3191
  1631
    const char* addr_curr = address;
neal@3191
  1632
    char* retval_curr = retval;
neal@3191
  1633
neal@3191
  1634
    while (addr_curr < at) {
neal@3191
  1635
        if (*addr_curr == '.') {
neal@3191
  1636
            addr_curr++;
neal@3191
  1637
            continue;
neal@3191
  1638
        }
neal@3191
  1639
        *retval_curr = *addr_curr;
neal@3191
  1640
        retval_curr++;
neal@3191
  1641
        addr_curr++;
neal@3191
  1642
    }
neal@3191
  1643
    if (*addr_curr == '@')
neal@3191
  1644
        strcat(retval_curr, addr_curr);
neal@3191
  1645
neal@3191
  1646
    return retval;
neal@3191
  1647
}
neal@3191
  1648
us@3209
  1649
static stringpair_list_t *add_key(PEP_SESSION session,
us@3209
  1650
                                  stringpair_list_t *keyinfo_list,
us@3209
  1651
                                  stringlist_t* keylist,
neal@3332
  1652
                                  pgp_tpk_t tpk, pgp_fingerprint_t fpr) {
us@3209
  1653
    bool revoked = false;
us@3209
  1654
    // Don't add revoked keys to the keyinfo_list.
us@3209
  1655
    if (keyinfo_list) {
neal@3332
  1656
        pgp_revocation_status_t rs = pgp_tpk_revocation_status(tpk);
neal@3332
  1657
        pgp_revocation_status_variant_t rsv = pgp_revocation_status_variant(rs);
neal@3332
  1658
        pgp_revocation_status_free(rs);
neal@3332
  1659
        if (rsv == PGP_REVOCATION_STATUS_REVOKED)
us@3209
  1660
            revoked = true;
us@3209
  1661
    }
us@3209
  1662
us@3209
  1663
    if (revoked && ! keylist)
us@3209
  1664
        return keyinfo_list;
us@3209
  1665
us@3209
  1666
    int dealloc_fpr = 0;
us@3209
  1667
    if (!fpr) {
us@3209
  1668
        dealloc_fpr = 1;
neal@3332
  1669
        fpr = pgp_tpk_fingerprint(tpk);
us@3209
  1670
    }
neal@3332
  1671
    char *fpr_str = pgp_fingerprint_to_hex(fpr);
us@3209
  1672
us@3209
  1673
    if (!revoked && keyinfo_list) {
neal@3332
  1674
        char *user_id = pgp_tpk_primary_user_id(tpk);
us@3209
  1675
        if (user_id)
us@3209
  1676
            keyinfo_list = stringpair_list_add(keyinfo_list,
us@3209
  1677
                                               new_stringpair(fpr_str, user_id));
us@3209
  1678
        free(user_id);
us@3209
  1679
    }
us@3209
  1680
us@3209
  1681
    if (keylist)
us@3209
  1682
        keylist = stringlist_add(keylist, fpr_str);
us@3209
  1683
us@3209
  1684
    free(fpr_str);
us@3209
  1685
    if (dealloc_fpr)
neal@3332
  1686
        pgp_fingerprint_free(fpr);
us@3209
  1687
us@3209
  1688
    return keyinfo_list;
us@3209
  1689
}
neal@3191
  1690
us@3209
  1691
static PEP_STATUS list_keys(PEP_SESSION session,
us@3209
  1692
                            const char* pattern, int private_only,
us@3209
  1693
                            stringpair_list_t** keyinfo_list, stringlist_t** keylist)
us@3209
  1694
{
us@3209
  1695
    PEP_STATUS status = PEP_STATUS_OK;
neal@3332
  1696
    pgp_tpk_t tpk = NULL;
neal@3332
  1697
    pgp_fingerprint_t fpr = NULL;
neal@3191
  1698
us@3209
  1699
    T("('%s', private: %d)", pattern, private_only);
neal@3191
  1700
us@3209
  1701
    stringpair_list_t* _keyinfo_list = NULL;
us@3209
  1702
    if (keyinfo_list) {
us@3209
  1703
        _keyinfo_list = new_stringpair_list(NULL);
us@3209
  1704
        if (!_keyinfo_list)
neal@3332
  1705
            ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "new_stringpair_list");
us@3209
  1706
    }
us@3209
  1707
    stringlist_t* _keylist = NULL;
us@3209
  1708
    if (keylist) {
us@3209
  1709
        _keylist = new_stringlist(NULL);
us@3209
  1710
        if (!_keylist)
neal@3332
  1711
            ERROR_OUT(NULL, PEP_OUT_OF_MEMORY, "new_string_list");
us@3209
  1712
    }
us@3209
  1713
us@3209
  1714
    // Trim any leading space.  This also makes it easier to recognize
us@3209
  1715
    // a string that is only whitespace.
us@3209
  1716
    while (*pattern == ' ')
us@3209
  1717
        pattern ++;
us@3209
  1718
us@3209
  1719
    if (strchr(pattern, '@')) {
us@3209
  1720
        // Looks like a mailbox.
neal@3332
  1721
        pgp_tpk_t *tpks = NULL;
us@3209
  1722
        int count = 0;
us@3209
  1723
        status = tpk_find_by_email(session, pattern, private_only, &tpks, &count);
neal@3332
  1724
        ERROR_OUT(NULL, status, "Looking up '%s'", pattern);
us@3209
  1725
        for (int i = 0; i < count; i ++) {
us@3209
  1726
            add_key(session, _keyinfo_list, _keylist, tpks[i], NULL);
neal@3332
  1727
            pgp_tpk_free(tpks[i]);
us@3209
  1728
        }
us@3209
  1729
        free(tpks);
us@3209
  1730
us@3209
  1731
        if (count == 0) {
neal@3191
  1732
            // If match failed, check to see if we've got a dotted
neal@3191
  1733
            // address in the pattern.  If so, try again without dots.
neal@3191
  1734
            const char* dotpos = strstr(pattern, ".");
neal@3191
  1735
            const char* atpos = strstr(pattern, "@");
neal@3191
  1736
            if (dotpos && atpos && (dotpos < atpos)) {
neal@3191
  1737
                char* undotted = _undot_address(pattern);
neal@3191
  1738
                if (undotted) {
us@3209
  1739
                    PEP_STATUS status = list_keys(session, undotted, private_only,
us@3209
  1740
                                                  keyinfo_list, keylist);
neal@3191
  1741
                    free(undotted);
neal@3191
  1742
                    return status;
neal@3191
  1743
                }
neal@3191
  1744
            }
neal@3191
  1745
        }
us@3209
  1746
    } else if (// Only hex characters and spaces
us@3209
  1747
               pattern[strspn(pattern, "0123456789aAbBcCdDeEfF ")] == 0
us@3209
  1748
               // And a fair amount of them.
us@3209
  1749
               && strlen(pattern) >= 16) {
us@3209
  1750
        // Fingerprint.
neal@3332
  1751
        fpr = pgp_fingerprint_from_hex(pattern);
us@3209
  1752
        status = tpk_find_by_fpr(session, fpr, false, &tpk, NULL);
neal@3332
  1753
        ERROR_OUT(NULL, status, "Looking up key");
us@3209
  1754
        add_key(session, _keyinfo_list, _keylist, tpk, fpr);
us@3209
  1755
    } else if (pattern[0] == 0) {
us@3209
  1756
        // Empty string.
neal@3191
  1757
neal@3332
  1758
        pgp_tpk_t *tpks = NULL;
us@3209
  1759
        int count = 0;
us@3209
  1760
        status = tpk_all(session, private_only, &tpks, &count);
neal@3332
  1761
        ERROR_OUT(NULL, status, "Looking up '%s'", pattern);
us@3209
  1762
        for (int i = 0; i < count; i ++) {
us@3209
  1763
            add_key(session, _keyinfo_list, _keylist, tpks[i], NULL);
neal@3332
  1764
            pgp_tpk_free(tpks[i]);
us@3209
  1765
        }
us@3209
  1766
        free(tpks);
us@3209
  1767
    } else {
us@3209
  1768
        T("unsupported pattern '%s'", pattern);
neal@3191
  1769
    }
neal@3191
  1770
neal@3191
  1771
 out:
neal@3191
  1772
    if (tpk)
neal@3332
  1773
        pgp_tpk_free(tpk);
us@3209
  1774
    if (fpr)
neal@3332
  1775
        pgp_fingerprint_free(fpr);
us@3209
  1776
us@3209
  1777
    if (status == PEP_KEY_NOT_FOUND)
us@3209
  1778
        status = PEP_STATUS_OK;
us@3209
  1779
us@3209
  1780
    if (status != PEP_STATUS_OK || (_keyinfo_list && !_keyinfo_list->value)) {
us@3209
  1781
        free_stringpair_list(_keyinfo_list);
us@3209
  1782
        _keyinfo_list = NULL;
us@3209
  1783
    }
us@3209
  1784
    if (keyinfo_list)
us@3209
  1785
        *keyinfo_list = _keyinfo_list;
neal@3191
  1786
us@3209
  1787
    if (status != PEP_STATUS_OK || (_keylist && !_keylist->value)) {
us@3209
  1788
        free_stringlist(_keylist);
us@3209
  1789
        _keylist = NULL;
us@3209
  1790
    }
us@3209
  1791
    if (keylist)
us@3209
  1792
        *keylist = _keylist;
us@3209
  1793
us@3209
  1794
    int len = -1;
us@3209
  1795
    if (keylist)
us@3209
  1796
        len = stringlist_length(*keylist);
us@3209
  1797
    else if (keyinfo_list)
us@3209
  1798
        len = stringpair_list_length(*keyinfo_list);
us@3209
  1799
    T("(%s) -> %s (%d keys)", pattern, pep_status_to_string(status), len);
neal@3191
  1800
    return status;
neal@3191
  1801
}
neal@3191
  1802
us@3209
  1803
// pattern could be empty, an fpr, or a mailbox.
us@3209
  1804
//
us@3209
  1805
// keyinfo_list is a list of <fpr, openpgp userid> tuples for the
us@3209
  1806
// matching keys.
us@3209
  1807
//
us@3209
  1808
// This function filters out revoked key, but not expired keys.
us@3209
  1809
PEP_STATUS pgp_list_keyinfo(PEP_SESSION session,
us@3209
  1810
                            const char* pattern,
us@3209
  1811
                            stringpair_list_t** keyinfo_list)
us@3209
  1812
{
us@3209
  1813
    return list_keys(session, pattern, false, keyinfo_list, NULL);
us@3209
  1814
}
us@3209
  1815
us@3209
  1816
PEP_STATUS pgp_recv_key(PEP_SESSION session, const char *pattern)
us@3209
  1817
{
us@3209
  1818
    assert(!"pgp_recv_key not implemented");
us@3209
  1819
    return PEP_UNKNOWN_ERROR;
us@3209
  1820
}
us@3209
  1821
us@3209
  1822
// Unlike pgp_list_keyinfo, this function returns revoked keys.
neal@3191
  1823
PEP_STATUS pgp_find_keys(
neal@3191
  1824
    PEP_SESSION session, const char *pattern, stringlist_t **keylist)
neal@3191
  1825
{
us@3209
  1826
    return list_keys(session, pattern, false, NULL, keylist);
neal@3191
  1827
}
neal@3191
  1828
us@3209
  1829
// Unlike pgp_list_keyinfo, this function returns revoked keys.
neal@3191
  1830
PEP_STATUS pgp_find_private_keys(
neal@3191
  1831
    PEP_SESSION session, const char *pattern, stringlist_t **keylist)
neal@3191
  1832
{
us@3209
  1833
    return list_keys(session, pattern, true, NULL, keylist);
neal@3191
  1834
}
neal@3191
  1835
neal@3191
  1836
PEP_STATUS pgp_send_key(PEP_SESSION session, const char *pattern)
neal@3191
  1837
{
neal@3191
  1838
    assert(!"pgp_send_key not implemented");
neal@3191
  1839
    return PEP_UNKNOWN_ERROR;
neal@3191
  1840
}
neal@3191
  1841
neal@3191
  1842
PEP_STATUS pgp_get_key_rating(
neal@3191
  1843
    PEP_SESSION session, const char *fpr, PEP_comm_type *comm_type)
neal@3191
  1844
{
neal@3191
  1845
    PEP_STATUS status = PEP_STATUS_OK;
neal@3332
  1846
    pgp_tpk_t tpk = NULL;
neal@3191
  1847
neal@3191
  1848
    assert(session);
neal@3191
  1849
    assert(fpr);
neal@3191
  1850
    assert(comm_type);
neal@3191
  1851
neal@3191
  1852
    *comm_type = PEP_ct_unknown;
neal@3191
  1853
neal@3332
  1854
    pgp_fingerprint_t pgp_fpr = pgp_fingerprint_from_hex(fpr);
neal@3332
  1855
    status = tpk_find_by_fpr(session, pgp_fpr, false, &tpk, NULL);
neal@3332
  1856
    pgp_fingerprint_free(pgp_fpr);
neal@3332
  1857
    ERROR_OUT(NULL, status, "Looking up key: %s", fpr);
neal@3191
  1858
neal@3191
  1859
    *comm_type = PEP_ct_OpenPGP_unconfirmed;
neal@3191
  1860
neal@3332
  1861
    if (pgp_tpk_expired(tpk)) {
neal@3191
  1862
        *comm_type = PEP_ct_key_expired;
neal@3191
  1863
        goto out;
neal@3191
  1864
    }
neal@3191
  1865
neal@3332
  1866
    pgp_revocation_status_t rs = pgp_tpk_revocation_status(tpk);
neal@3332
  1867
    pgp_revocation_status_variant_t rsv = pgp_revocation_status_variant(rs);
neal@3332
  1868
    pgp_revocation_status_free(rs);
neal@3332
  1869
    if (rsv == PGP_REVOCATION_STATUS_REVOKED) {
neal@3191
  1870
        *comm_type = PEP_ct_key_revoked;
neal@3191
  1871
        goto out;
neal@3191
  1872
    }
neal@3191
  1873
neal@3191
  1874
    PEP_comm_type best_enc = PEP_ct_no_encryption, best_sign = PEP_ct_no_encryption;
neal@3332
  1875
    pgp_tpk_key_iter_t key_iter = pgp_tpk_key_iter(tpk);
neal@3332
  1876
    pgp_key_t key;
neal@3332
  1877
    pgp_signature_t sig;
neal@3332
  1878
    pgp_revocation_status_t rev;
neal@3332
  1879
    while ((key = pgp_tpk_key_iter_next(key_iter, &sig, &rev))) {
neal@3191
  1880
        if (! sig)
neal@3191
  1881
            continue;
neal@3191
  1882
neal@3332
  1883
        if (pgp_revocation_status_variant(rev) == PGP_REVOCATION_STATUS_REVOKED)
neal@3191
  1884
            continue;
neal@3191
  1885
neal@3332
  1886
        if (! pgp_signature_key_alive(sig, key))
neal@3191
  1887
            continue;
neal@3191
  1888
neal@3191
  1889
        PEP_comm_type curr = PEP_ct_no_encryption;
neal@3191
  1890
neal@3332
  1891
        int can_enc = pgp_signature_can_encrypt_for_transport(sig)
neal@3332
  1892
            || pgp_signature_can_encrypt_at_rest(sig);
neal@3332
  1893
        int can_sign = pgp_signature_can_sign(sig);
neal@3191
  1894
neal@3332
  1895
        pgp_public_key_algo_t pk_algo = pgp_key_public_key_algo(key);
neal@3332
  1896
        if (pk_algo == PGP_PUBLIC_KEY_ALGO_RSA_ENCRYPT_SIGN
neal@3332
  1897
            || pk_algo == PGP_PUBLIC_KEY_ALGO_RSA_ENCRYPT
neal@3332
  1898
            || pk_algo == PGP_PUBLIC_KEY_ALGO_RSA_SIGN) {
neal@3332
  1899
            int bits = pgp_key_public_key_bits(key);
neal@3191
  1900
            if (bits < 1024)
neal@3191
  1901
                curr = PEP_ct_key_too_short;
neal@3191
  1902
            else if (bits == 1024)
neal@3191
  1903
                curr = PEP_ct_OpenPGP_weak_unconfirmed;
neal@3191
  1904
            else
neal@3191
  1905
                curr = PEP_ct_OpenPGP_unconfirmed;
neal@3191
  1906
        } else {
neal@3191
  1907
            curr = PEP_ct_OpenPGP_unconfirmed;
neal@3191
  1908
        }
neal@3191
  1909
neal@3191
  1910
        if (can_enc)
neal@3191
  1911
            best_enc = _MAX(best_enc, curr);
neal@3191
  1912
neal@3191
  1913
        if (can_sign)
neal@3191
  1914
            best_sign = _MAX(best_sign, curr);
neal@3191
  1915
    }
neal@3332
  1916
    pgp_tpk_key_iter_free(key_iter);
neal@3191
  1917
neal@3191
  1918
    if (best_enc == PEP_ct_no_encryption || best_sign == PEP_ct_no_encryption) {
neal@3191
  1919
        *comm_type = PEP_ct_key_b0rken;
neal@3191
  1920
        goto out;
neal@3191
  1921
    } else {
neal@3191
  1922
        *comm_type = _MIN(best_enc, best_sign);
neal@3191
  1923
    }
neal@3191
  1924
neal@3191
  1925
 out:
neal@3191
  1926
    if (tpk)
neal@3332
  1927
        pgp_tpk_free(tpk);
neal@3191
  1928
neal@3191
  1929
    T("(%s) -> %s", fpr, pep_comm_type_to_string(*comm_type));
neal@3191
  1930
    return status;
neal@3191
  1931
}
neal@3191
  1932
neal@3191
  1933
neal@3191
  1934
PEP_STATUS pgp_renew_key(
neal@3191
  1935
    PEP_SESSION session, const char *fpr, const timestamp *ts)
neal@3191
  1936
{
neal@3191
  1937
    PEP_STATUS status = PEP_STATUS_OK;
neal@3332
  1938
    pgp_error_t err = NULL;
neal@3332
  1939
    pgp_tpk_t tpk = NULL;
neal@3191
  1940
    time_t t = mktime((struct tm *) ts);
neal@3191
  1941
us@3209
  1942
    T("(%s)", fpr);
us@3209
  1943
us@3209
  1944
    status = tpk_find_by_fpr_hex(session, fpr, true, &tpk, NULL);
neal@3332
  1945
    ERROR_OUT(NULL, status, "Looking up '%s'", fpr);
neal@3191
  1946
neal@3332
  1947
    uint32_t creation_time = pgp_key_creation_time(pgp_tpk_primary(tpk));
neal@3191
  1948
    if (creation_time > t)
neal@3191
  1949
        // The creation time is after the expiration time!
neal@3332
  1950
        ERROR_OUT(NULL, PEP_UNKNOWN_ERROR,
neal@3191
  1951
                  "creation time can't be after expiration time");
neal@3191
  1952
neal@3191
  1953
    uint32_t delta = t - creation_time;
neal@3332
  1954
    tpk = pgp_tpk_set_expiry(&err, tpk, delta);
neal@3191
  1955
    if (! tpk)
neal@3332
  1956
        ERROR_OUT(err, PEP_UNKNOWN_ERROR, "setting expiration");
neal@3191
  1957
us@3209
  1958
    status = tpk_save(session, tpk, NULL);
neal@3191
  1959
    tpk = NULL;
neal@3332
  1960
    ERROR_OUT(NULL, status, "Saving %s", fpr);
neal@3191
  1961
neal@3191
  1962
 out:
neal@3191
  1963
    if (tpk)
neal@3332
  1964
        pgp_tpk_free(tpk);
neal@3191
  1965
us@3209
  1966
    T("(%s) -> %s", fpr, pep_status_to_string(status));
neal@3191
  1967
    return status;
neal@3191
  1968
}
neal@3191
  1969
neal@3191
  1970
PEP_STATUS pgp_revoke_key(
neal@3191
  1971
    PEP_SESSION session, const char *fpr, const char *reason)
neal@3191
  1972
{
neal@3191
  1973
    PEP_STATUS status = PEP_STATUS_OK;
neal@3332
  1974
    pgp_error_t err = NULL;
neal@3332
  1975
    pgp_tpk_t tpk = NULL;
neal@3191
  1976
us@3209
  1977
    T("(%s)", fpr);
us@3209
  1978
us@3209
  1979
    status = tpk_find_by_fpr_hex(session, fpr, true, &tpk, NULL);
neal@3332
  1980
    ERROR_OUT(NULL, status, "Looking up %s", fpr);
neal@3332
  1981
neal@3332
  1982
    pgp_key_t primary_key = pgp_tpk_primary (tpk);
neal@3332
  1983
    pgp_key_pair_t primary_keypair
neal@3332
  1984
        = pgp_key_into_key_pair (NULL, pgp_key_clone (primary_key));
neal@3332
  1985
    pgp_key_free (primary_key);
neal@3332
  1986
    assert (primary_keypair);
neal@3332
  1987
    pgp_signer_t primary_signer = pgp_key_pair_as_signer (primary_keypair);
neal@3191
  1988
neal@3332
  1989
    tpk = pgp_tpk_revoke_in_place(&err, tpk, primary_signer,
neal@3332
  1990
                                  PGP_REASON_FOR_REVOCATION_UNSPECIFIED,
neal@3332
  1991
                                  reason);
neal@3332
  1992
    pgp_signer_free (primary_signer);
neal@3332
  1993
    pgp_key_pair_free (primary_keypair);
neal@3191
  1994
    if (! tpk)
neal@3332
  1995
        ERROR_OUT(err, PEP_UNKNOWN_ERROR, "setting expiration");
neal@3191
  1996
neal@3332
  1997
    assert(pgp_revocation_status_variant(pgp_tpk_revocation_status(tpk))
neal@3332
  1998
           == PGP_REVOCATION_STATUS_REVOKED);
neal@3191
  1999
us@3209
  2000
    status = tpk_save(session, tpk, NULL);
neal@3191
  2001
    tpk = NULL;
neal@3332
  2002
    ERROR_OUT(NULL, status, "Saving %s", fpr);
neal@3191
  2003
neal@3191
  2004
 out:
neal@3191
  2005
    if (tpk)
neal@3332
  2006
        pgp_tpk_free(tpk);
neal@3191
  2007
us@3209
  2008
    T("(%s) -> %s", fpr, pep_status_to_string(status));
neal@3191
  2009
    return status;
neal@3191
  2010
}
neal@3191
  2011
neal@3191
  2012
PEP_STATUS pgp_key_expired(PEP_SESSION session, const char *fpr,
neal@3191
  2013
                           const time_t when, bool *expired)
neal@3191
  2014
{
neal@3191
  2015
    PEP_STATUS status = PEP_STATUS_OK;
neal@3332
  2016
    pgp_tpk_t tpk = NULL;
us@3209
  2017
    T("(%s)", fpr);
neal@3191
  2018
neal@3191
  2019
    assert(session);
neal@3191
  2020
    assert(fpr);
neal@3191
  2021
    assert(expired);
neal@3191
  2022
neal@3191
  2023
    *expired = false;
neal@3191
  2024
neal@3332
  2025
    pgp_fingerprint_t pgp_fpr = pgp_fingerprint_from_hex(fpr);
neal@3332
  2026
    status = tpk_find_by_fpr(session, pgp_fpr, false, &tpk, NULL);
neal@3332
  2027
    pgp_fingerprint_free(pgp_fpr);
neal@3332
  2028
    ERROR_OUT(NULL, status, "Looking up %s", fpr);
neal@3191
  2029
neal@3191
  2030
    // Is the TPK live?
neal@3332
  2031
    *expired = !pgp_tpk_alive_at(tpk, when);
neal@3191
  2032
    if (*expired)
neal@3191
  2033
        goto out;
neal@3191
  2034
neal@3191
  2035
    // Are there at least one certification subkey, one signing subkey
neal@3191
  2036
    // and one encryption subkey that are live?
neal@3191
  2037
    int can_certify = 0, can_encrypt = 0, can_sign = 0;
neal@3191
  2038
neal@3332
  2039
    pgp_tpk_key_iter_t key_iter = pgp_tpk_key_iter(tpk);
neal@3332
  2040
    pgp_key_t key;
neal@3332
  2041
    pgp_signature_t sig;
neal@3332
  2042
    pgp_revocation_status_t rev;
neal@3332
  2043
    while ((key = pgp_tpk_key_iter_next(key_iter, &sig, &rev))) {
neal@3191
  2044
        if (! sig)
neal@3191
  2045
            continue;
neal@3191
  2046
neal@3332
  2047
        if (pgp_revocation_status_variant(rev) == PGP_REVOCATION_STATUS_REVOKED)
neal@3191
  2048
            continue;
neal@3191
  2049
neal@3332
  2050
        if (!pgp_signature_key_alive_at(sig, key, when))
neal@3191
  2051
            continue;
neal@3191
  2052
neal@3332
  2053
        if (pgp_signature_can_encrypt_for_transport(sig)
neal@3332
  2054
            || pgp_signature_can_encrypt_at_rest(sig))
neal@3191
  2055
            can_encrypt = 1;
neal@3332
  2056
        if (pgp_signature_can_sign(sig))
neal@3191
  2057
            can_sign = 1;
neal@3332
  2058
        if (pgp_signature_can_certify(sig))
neal@3191
  2059
            can_certify = 1;
neal@3191
  2060
neal@3191
  2061
        if (can_encrypt && can_sign && can_certify)
neal@3191
  2062
            break;
neal@3191
  2063
    }
neal@3332
  2064
    pgp_tpk_key_iter_free(key_iter);
neal@3191
  2065
neal@3191
  2066
    *expired = !(can_encrypt && can_sign && can_certify);
neal@3191
  2067
neal@3191
  2068
 out:
neal@3191
  2069
    if (tpk)
neal@3332
  2070
        pgp_tpk_free(tpk);
us@3209
  2071
    T("(%s) -> %s", fpr, pep_status_to_string(status));
neal@3191
  2072
    return status;
neal@3191
  2073
}
neal@3191
  2074
neal@3191
  2075
PEP_STATUS pgp_key_revoked(PEP_SESSION session, const char *fpr, bool *revoked)
neal@3191
  2076
{
neal@3191
  2077
    PEP_STATUS status = PEP_STATUS_OK;
neal@3332
  2078
    pgp_tpk_t tpk;
neal@3191
  2079
us@3209
  2080
    T("(%s)", fpr);
us@3209
  2081
neal@3191
  2082
    assert(session);
neal@3191
  2083
    assert(fpr);
neal@3191
  2084
    assert(revoked);
neal@3191
  2085
neal@3191
  2086
    *revoked = false;
neal@3191
  2087
neal@3332
  2088
    pgp_fingerprint_t pgp_fpr = pgp_fingerprint_from_hex(fpr);
neal@3332
  2089
    status = tpk_find_by_fpr(session, pgp_fpr, false, &tpk, NULL);
neal@3332
  2090
    pgp_fingerprint_free(pgp_fpr);
neal@3332
  2091
    ERROR_OUT(NULL, status, "Looking up %s", fpr);
neal@3191
  2092
neal@3332
  2093
    pgp_revocation_status_t rs = pgp_tpk_revocation_status(tpk);
neal@3332
  2094
    *revoked = pgp_revocation_status_variant(rs) == PGP_REVOCATION_STATUS_REVOKED;
neal@3332
  2095
    pgp_revocation_status_free (rs);
neal@3332
  2096
    pgp_tpk_free(tpk);
neal@3191
  2097
neal@3191
  2098
 out:
us@3209
  2099
    T("(%s) -> %s", fpr, pep_status_to_string(status));
neal@3191
  2100
    return status;
neal@3191
  2101
}
neal@3191
  2102
neal@3191
  2103
PEP_STATUS pgp_key_created(PEP_SESSION session, const char *fpr, time_t *created)
neal@3191
  2104
{
neal@3191
  2105
    PEP_STATUS status = PEP_STATUS_OK;
neal@3332
  2106
    pgp_tpk_t tpk = NULL;
us@3209
  2107
    T("(%s)", fpr);
neal@3191
  2108
neal@3191
  2109
    *created = 0;
neal@3191
  2110
neal@3332
  2111
    pgp_fingerprint_t pgp_fpr = pgp_fingerprint_from_hex(fpr);
neal@3332
  2112
    status = tpk_find_by_fpr(session, pgp_fpr, false, &tpk, NULL);
neal@3332
  2113
    pgp_fingerprint_free(pgp_fpr);
neal@3332
  2114
    ERROR_OUT(NULL, status, "Looking up %s", fpr);
neal@3191
  2115
neal@3332
  2116
    pgp_key_t k = pgp_tpk_primary(tpk);
neal@3332
  2117
    *created = pgp_key_creation_time(k);
neal@3332
  2118
    pgp_tpk_free(tpk);
neal@3191
  2119
neal@3191
  2120
 out:
us@3209
  2121
    T("(%s) -> %s", fpr, pep_status_to_string(status));
neal@3191
  2122
    return status;
neal@3191
  2123
}
neal@3191
  2124
neal@3191
  2125
PEP_STATUS pgp_binary(const char **path)
neal@3191
  2126
{
neal@3191
  2127
    return PEP_STATUS_OK;
neal@3191
  2128
}
neal@3191
  2129
neal@3191
  2130
PEP_STATUS pgp_contains_priv_key(PEP_SESSION session, const char *fpr,
neal@3191
  2131
                                 bool *has_private)
neal@3191
  2132
{
us@3209
  2133
    T("(%s)", fpr);
neal@3332
  2134
    pgp_fingerprint_t pgp_fpr = pgp_fingerprint_from_hex(fpr);
neal@3332
  2135
    PEP_STATUS status = tpk_find_by_fpr(session, pgp_fpr, true, NULL, NULL);
neal@3332
  2136
    pgp_fingerprint_free(pgp_fpr);
neal@3191
  2137
    if (status == PEP_STATUS_OK) {
neal@3191
  2138
        *has_private = 1;
neal@3191
  2139
    } else if (status == PEP_KEY_NOT_FOUND) {
neal@3191
  2140
        *has_private = 0;
us@3209
  2141
        status = PEP_STATUS_OK;
neal@3191
  2142
    }
neal@3332
  2143
    T("(%s) -> %s, %s",
neal@3332
  2144
      fpr, *has_private ? "priv" : "pub", pep_status_to_string(status));
us@3209
  2145
    return status;
neal@3191
  2146
}