src/pgp_sequoia.c
author Neal H. Walfield <neal@pep.foundation>
Fri, 28 Dec 2018 12:47:31 +0100
branchsync
changeset 3213 e56efa502f08
parent 3212 fb4124923112
child 3272 4d6c07372e3e
permissions -rw-r--r--
Enable secure_delete and foreign_keys on the Sequoia key DB.

- Secure delete is important since we are storing private keys.

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