src/pgp_sequoia.c
author Neal H. Walfield <neal@pep.foundation>
Fri, 28 Dec 2018 12:40:23 +0100
branchsync
changeset 3212 fb4124923112
parent 3211 8ca54e726b3c
child 3213 e56efa502f08
permissions -rw-r--r--
Simplify finalization of sqlite statements.

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