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