Replace use of Sequoia's backend with a custom key store. sync
authorus@chu.huenfield.org
Tue, 25 Dec 2018 14:46:45 +0100
branchsync
changeset 3209c15b4ca2b52a
parent 3198 809a64d99d68
child 3210 206d68617cf8
Replace use of Sequoia's backend with a custom key store.

- Sequoia's key store doesn't meet pep's needs (in particular, the
ability to search on a key's user id) and trying to shoehorn pep's
needs onto Sequoia's key store abstractions is just introducing
overhead with no appreciable gain in functionality.

- This patch changes the Sequoia backend to use a local sqlite
database to store the public keys.
src/pEp_internal.h
src/pgp_sequoia.c
     1.1 --- a/src/pEp_internal.h	Thu Dec 20 15:28:21 2018 +0100
     1.2 +++ b/src/pEp_internal.h	Tue Dec 25 14:46:45 2018 +0100
     1.3 @@ -129,15 +129,21 @@
     1.4      pEpNetPGPSession ctx;
     1.5  #elif defined(USE_SEQUOIA)
     1.6      sq_context_t ctx;
     1.7 -    sq_store_t store;
     1.8      sqlite3 *key_db;
     1.9      sqlite3_stmt *begin_transaction;
    1.10      sqlite3_stmt *commit_transaction;
    1.11      sqlite3_stmt *rollback_transaction;
    1.12 -    sqlite3_stmt *tsk_save_insert_primary;
    1.13 -    sqlite3_stmt *tsk_save_insert_subkeys;
    1.14 +    sqlite3_stmt *tpk_find;
    1.15 +    sqlite3_stmt *tsk_find;
    1.16 +    sqlite3_stmt *tpk_find_by_keyid;
    1.17 +    sqlite3_stmt *tsk_find_by_keyid;
    1.18 +    sqlite3_stmt *tpk_find_by_email;
    1.19 +    sqlite3_stmt *tsk_find_by_email;
    1.20 +    sqlite3_stmt *tpk_all;
    1.21      sqlite3_stmt *tsk_all;
    1.22 -    sqlite3_stmt *tsk_find_by_keyid;
    1.23 +    sqlite3_stmt *tpk_save_insert_primary;
    1.24 +    sqlite3_stmt *tpk_save_insert_subkeys;
    1.25 +    sqlite3_stmt *tpk_save_insert_userids;
    1.26  #endif
    1.27  
    1.28      PEP_cryptotech_t *cryptotech;
     2.1 --- a/src/pgp_sequoia.c	Thu Dec 20 15:28:21 2018 +0100
     2.2 +++ b/src/pgp_sequoia.c	Tue Dec 25 14:46:45 2018 +0100
     2.3 @@ -72,11 +72,6 @@
     2.4          ERROR_OUT(session, PEP_INIT_GPGME_INIT_FAILED,
     2.5                    "initializing sequoia context");
     2.6  
     2.7 -    session->store = sq_store_open(session->ctx, "foundation.pep");
     2.8 -    if (session->store == NULL)
     2.9 -        ERROR_OUT(session, PEP_INIT_GPGME_INIT_FAILED, "opening the store");
    2.10 -
    2.11 -
    2.12      // Create the home directory.
    2.13      char *home_env = getenv("HOME");
    2.14      if (!home_env)
    2.15 @@ -114,9 +109,12 @@
    2.16  
    2.17      sqlite_result = sqlite3_exec(session->key_db,
    2.18                                   "CREATE TABLE IF NOT EXISTS keys (\n"
    2.19 -                                 "   primary_key TEXT PRIMARY KEY,\n"
    2.20 -                                 "   tsk BLOB\n"
    2.21 -                                 ");\n",
    2.22 +                                 "   primary_key TEXT UNIQUE PRIMARY KEY,\n"
    2.23 +                                 "   secret BOOLEAN,\n"
    2.24 +                                 "   tpk BLOB\n"
    2.25 +                                 ");\n"
    2.26 +                                 "CREATE INDEX IF NOT EXISTS keys_index\n"
    2.27 +                                 "  ON keys (primary_key, secret)\n",
    2.28                                   NULL, NULL, NULL);
    2.29      if (sqlite_result != SQLITE_OK)
    2.30          ERROR_OUT(session, PEP_INIT_CANNOT_OPEN_DB,
    2.31 @@ -125,18 +123,38 @@
    2.32  
    2.33      sqlite_result = sqlite3_exec(session->key_db,
    2.34                                   "CREATE TABLE IF NOT EXISTS subkeys (\n"
    2.35 -                                 "   subkey TEXT PRIMARY KEY,\n"
    2.36 -                                 "   primary_key TEXT,\n"
    2.37 +                                 "   subkey TEXT NOT NULL,\n"
    2.38 +                                 "   primary_key TEXT NOT NULL,\n"
    2.39 +                                 "   UNIQUE(subkey, primary_key),\n"
    2.40                                   "   FOREIGN KEY (primary_key)\n"
    2.41                                   "       REFERENCES keys(primary_key)\n"
    2.42                                   "     ON DELETE CASCADE\n"
    2.43 -                                 ");\n",
    2.44 +                                 ");\n"
    2.45 +                                 "CREATE INDEX IF NOT EXISTS subkeys_index\n"
    2.46 +                                 "  ON subkeys (subkey, primary_key)\n",
    2.47                                   NULL, NULL, NULL);
    2.48      if (sqlite_result != SQLITE_OK)
    2.49          ERROR_OUT(session, PEP_INIT_CANNOT_OPEN_DB,
    2.50                    "creating subkeys table: %s",
    2.51                    sqlite3_errmsg(session->key_db));
    2.52  
    2.53 +    sqlite_result = sqlite3_exec(session->key_db,
    2.54 +                                 "CREATE TABLE IF NOT EXISTS userids (\n"
    2.55 +                                 "   userid TEXT NOT NULL,\n"
    2.56 +                                 "   primary_key TEXT NOT NULL,\n"
    2.57 +                                 "   UNIQUE(userid, primary_key),\n"
    2.58 +                                 "   FOREIGN KEY (primary_key)\n"
    2.59 +                                 "       REFERENCES keys(primary_key)\n"
    2.60 +                                 "     ON DELETE CASCADE\n"
    2.61 +                                 ");\n"
    2.62 +                                 "CREATE INDEX IF NOT EXISTS userids_index\n"
    2.63 +                                 "  ON userids (userid, primary_key)\n",
    2.64 +                                 NULL, NULL, NULL);
    2.65 +    if (sqlite_result != SQLITE_OK)
    2.66 +        ERROR_OUT(session, PEP_INIT_CANNOT_OPEN_DB,
    2.67 +                  "creating userids table: %s",
    2.68 +                  sqlite3_errmsg(session->key_db));
    2.69 +
    2.70      sqlite_result
    2.71          = sqlite3_prepare_v2(session->key_db, "begin transaction",
    2.72                               -1, &session->begin_transaction, NULL);
    2.73 @@ -154,10 +172,81 @@
    2.74  
    2.75      sqlite_result
    2.76          = sqlite3_prepare_v2(session->key_db,
    2.77 +                             "SELECT tpk, secret FROM keys"
    2.78 +                             " WHERE primary_key == ?",
    2.79 +                             -1, &session->tpk_find, NULL);
    2.80 +    assert(sqlite_result == SQLITE_OK);
    2.81 +
    2.82 +    sqlite_result
    2.83 +        = sqlite3_prepare_v2(session->key_db,
    2.84 +                             "SELECT tpk, secret FROM keys"
    2.85 +                             " WHERE primary_key == ? and secret == 1",
    2.86 +                             -1, &session->tsk_find, NULL);
    2.87 +    assert(sqlite_result == SQLITE_OK);
    2.88 +
    2.89 +    sqlite_result
    2.90 +        = sqlite3_prepare_v2(session->key_db,
    2.91 +                             "SELECT tpk, secret FROM subkeys"
    2.92 +                             " LEFT JOIN keys"
    2.93 +                             "  ON subkeys.primary_key == keys.primary_key"
    2.94 +                             " WHERE subkey == ?",
    2.95 +                             -1, &session->tpk_find_by_keyid, NULL);
    2.96 +    assert(sqlite_result == SQLITE_OK);
    2.97 +
    2.98 +    sqlite_result
    2.99 +        = sqlite3_prepare_v2(session->key_db,
   2.100 +                             "SELECT tpk, secret FROM subkeys"
   2.101 +                             " LEFT JOIN keys"
   2.102 +                             "  ON subkeys.primary_key == keys.primary_key"
   2.103 +                             " WHERE subkey == ?",
   2.104 +                             -1, &session->tpk_find_by_keyid, NULL);
   2.105 +    assert(sqlite_result == SQLITE_OK);
   2.106 +
   2.107 +    sqlite_result
   2.108 +        = sqlite3_prepare_v2(session->key_db,
   2.109 +                             "SELECT tpk, secret FROM subkeys"
   2.110 +                             " LEFT JOIN keys"
   2.111 +                             "  ON subkeys.primary_key == keys.primary_key"
   2.112 +                             " WHERE subkey == ? and keys.secret == 1",
   2.113 +                             -1, &session->tsk_find_by_keyid, NULL);
   2.114 +    assert(sqlite_result == SQLITE_OK);
   2.115 +
   2.116 +    sqlite_result
   2.117 +        = sqlite3_prepare_v2(session->key_db,
   2.118 +                             "SELECT tpk, secret FROM userids"
   2.119 +                             " LEFT JOIN keys"
   2.120 +                             "  ON userids.primary_key == keys.primary_key"
   2.121 +                             " WHERE userid == ?",
   2.122 +                             -1, &session->tpk_find_by_email, NULL);
   2.123 +    assert(sqlite_result == SQLITE_OK);
   2.124 +
   2.125 +    sqlite_result
   2.126 +        = sqlite3_prepare_v2(session->key_db,
   2.127 +                             "SELECT tpk, secret FROM userids"
   2.128 +                             " LEFT JOIN keys"
   2.129 +                             "  ON userids.primary_key == keys.primary_key"
   2.130 +                             " WHERE userid == ? and keys.secret == 1",
   2.131 +                             -1, &session->tsk_find_by_email, NULL);
   2.132 +    assert(sqlite_result == SQLITE_OK);
   2.133 +
   2.134 +    sqlite_result
   2.135 +        = sqlite3_prepare_v2(session->key_db,
   2.136 +                             "select tpk, secret from keys",
   2.137 +                             -1, &session->tpk_all, NULL);
   2.138 +    assert(sqlite_result == SQLITE_OK);
   2.139 +
   2.140 +    sqlite_result
   2.141 +        = sqlite3_prepare_v2(session->key_db,
   2.142 +                             "select tpk, secret from keys where secret = 1",
   2.143 +                             -1, &session->tsk_all, NULL);
   2.144 +    assert(sqlite_result == SQLITE_OK);
   2.145 +
   2.146 +    sqlite_result
   2.147 +        = sqlite3_prepare_v2(session->key_db,
   2.148                               "INSERT OR REPLACE INTO keys"
   2.149 -                             "   (primary_key, tsk)"
   2.150 -                             " VALUES (?, ?)",
   2.151 -                             -1, &session->tsk_save_insert_primary, NULL);
   2.152 +                             "   (primary_key, secret, tpk)"
   2.153 +                             " VALUES (?, ?, ?)",
   2.154 +                             -1, &session->tpk_save_insert_primary, NULL);
   2.155      assert(sqlite_result == SQLITE_OK);
   2.156  
   2.157      sqlite_result
   2.158 @@ -165,22 +254,15 @@
   2.159                               "INSERT OR REPLACE INTO subkeys"
   2.160                               "   (subkey, primary_key)"
   2.161                               " VALUES (?, ?)",
   2.162 -                             -1, &session->tsk_save_insert_subkeys, NULL);
   2.163 -    assert(sqlite_result == SQLITE_OK);
   2.164 -
   2.165 -    sqlite_result
   2.166 -        = sqlite3_prepare_v2(session->key_db, "select tsk from keys",
   2.167 -                             -1, &session->tsk_all, NULL);
   2.168 +                             -1, &session->tpk_save_insert_subkeys, NULL);
   2.169      assert(sqlite_result == SQLITE_OK);
   2.170  
   2.171      sqlite_result
   2.172          = sqlite3_prepare_v2(session->key_db,
   2.173 -                             "SELECT keys.tsk FROM subkeys"
   2.174 -                             " LEFT JOIN keys"
   2.175 -                             "  ON subkeys.primary_key"
   2.176 -                             "     == keys.primary_key"
   2.177 -                             " WHERE subkey == ?",
   2.178 -                             -1, &session->tsk_find_by_keyid, NULL);
   2.179 +                             "INSERT OR REPLACE INTO userids"
   2.180 +                             "   (userid, primary_key)"
   2.181 +                             " VALUES (?, ?)",
   2.182 +                             -1, &session->tpk_save_insert_userids, NULL);
   2.183      assert(sqlite_result == SQLITE_OK);
   2.184  
   2.185   out:
   2.186 @@ -200,18 +282,44 @@
   2.187      if (session->rollback_transaction)
   2.188          sqlite3_finalize(session->rollback_transaction);
   2.189      session->rollback_transaction = NULL;
   2.190 -    if (session->tsk_save_insert_primary)
   2.191 -        sqlite3_finalize(session->tsk_save_insert_primary);
   2.192 -    session->tsk_save_insert_primary = NULL;
   2.193 -    if (session->tsk_save_insert_subkeys)
   2.194 -        sqlite3_finalize(session->tsk_save_insert_subkeys);
   2.195 -    session->tsk_save_insert_subkeys = NULL;
   2.196 +
   2.197 +    if (session->tpk_find)
   2.198 +        sqlite3_finalize(session->tpk_find);
   2.199 +    session->tpk_find = NULL;
   2.200 +    if (session->tsk_find)
   2.201 +        sqlite3_finalize(session->tsk_find);
   2.202 +    session->tsk_find = NULL;
   2.203 +
   2.204 +    if (session->tpk_find_by_keyid)
   2.205 +        sqlite3_finalize(session->tpk_find_by_keyid);
   2.206 +    session->tpk_find_by_keyid = NULL;
   2.207 +    if (session->tsk_find_by_keyid)
   2.208 +        sqlite3_finalize(session->tsk_find_by_keyid);
   2.209 +    session->tsk_find_by_keyid = NULL;
   2.210 +
   2.211 +    if (session->tpk_find_by_email)
   2.212 +        sqlite3_finalize(session->tpk_find_by_email);
   2.213 +    session->tpk_find_by_email = NULL;
   2.214 +    if (session->tsk_find_by_email)
   2.215 +        sqlite3_finalize(session->tsk_find_by_email);
   2.216 +    session->tsk_find_by_email = NULL;
   2.217 +
   2.218 +    if (session->tpk_all)
   2.219 +        sqlite3_finalize(session->tpk_all);
   2.220 +    session->tpk_all = NULL;
   2.221      if (session->tsk_all)
   2.222          sqlite3_finalize(session->tsk_all);
   2.223      session->tsk_all = NULL;
   2.224 -    if (session->tsk_find_by_keyid)
   2.225 -        sqlite3_finalize(session->tsk_find_by_keyid);
   2.226 -    session->tsk_find_by_keyid = NULL;
   2.227 +
   2.228 +    if (session->tpk_save_insert_primary)
   2.229 +        sqlite3_finalize(session->tpk_save_insert_primary);
   2.230 +    session->tpk_save_insert_primary = NULL;
   2.231 +    if (session->tpk_save_insert_subkeys)
   2.232 +        sqlite3_finalize(session->tpk_save_insert_subkeys);
   2.233 +    session->tpk_save_insert_subkeys = NULL;
   2.234 +    if (session->tpk_save_insert_userids)
   2.235 +        sqlite3_finalize(session->tpk_save_insert_userids);
   2.236 +    session->tpk_save_insert_userids = NULL;
   2.237  
   2.238      if (session->key_db) {
   2.239          int result = sqlite3_close_v2(session->key_db);
   2.240 @@ -222,11 +330,6 @@
   2.241          session->key_db = NULL;
   2.242      }
   2.243  
   2.244 -    if (session->store) {
   2.245 -        sq_store_free(session->store);
   2.246 -        session->store = NULL;
   2.247 -    }
   2.248 -
   2.249      if (session->ctx) {
   2.250          sq_context_free(session->ctx);
   2.251          session->ctx = NULL;
   2.252 @@ -300,37 +403,28 @@
   2.253      free(user_id);
   2.254  }
   2.255  
   2.256 -
   2.257 -// Returns the TSK identified by the provided keyid.
   2.258 -//
   2.259 -// If tsk is NULL, the TSK is not parsed and this function simply
   2.260 -// returns whether the key is locally available.
   2.261 -static PEP_STATUS tsk_find_by_keyid_hex(PEP_SESSION, const char *, sq_tsk_t *)
   2.262 -  __attribute__((nonnull(1, 2)));
   2.263 -static PEP_STATUS tsk_find_by_keyid_hex(
   2.264 -        PEP_SESSION session,
   2.265 -        const char *keyid_hex,
   2.266 -        sq_tsk_t *tsk)
   2.267 +// step statement and load the tpk and secret.
   2.268 +static PEP_STATUS key_load(PEP_SESSION, sqlite3_stmt *, sq_tpk_t *, int *)
   2.269 +    __attribute__((nonnull(1, 2)));
   2.270 +static PEP_STATUS key_load(PEP_SESSION session, sqlite3_stmt *stmt,
   2.271 +                           sq_tpk_t *tpkp, int *secretp)
   2.272  {
   2.273      PEP_STATUS status = PEP_STATUS_OK;
   2.274 -    T("%s", keyid_hex);
   2.275 -
   2.276 -    sqlite3_stmt *stmt = session->tsk_find_by_keyid;
   2.277 -    sqlite3_bind_text(stmt, 1, keyid_hex, -1, SQLITE_STATIC);
   2.278      int sqlite_result = sqlite3_step(stmt);
   2.279      switch (sqlite_result) {
   2.280      case SQLITE_ROW:
   2.281 -        if (tsk) {
   2.282 -            // Get the TSK from the first column.
   2.283 +        if (tpkp) {
   2.284              int data_len = sqlite3_column_bytes(stmt, 0);
   2.285              const void *data = sqlite3_column_blob(stmt, 0);
   2.286  
   2.287 -            sq_tpk_t tpk = sq_tpk_from_bytes(session->ctx, data, data_len);
   2.288 -            if (!tpk)
   2.289 +            *tpkp = sq_tpk_from_bytes(session->ctx, data, data_len);
   2.290 +            if (!*tpkp)
   2.291                  ERROR_OUT(session, PEP_GET_KEY_FAILED, "parsing TPK");
   2.292 +        }
   2.293  
   2.294 -            *tsk = sq_tpk_into_tsk(tpk);
   2.295 -        }
   2.296 +        if (secretp)
   2.297 +            *secretp = sqlite3_column_int(stmt, 1);
   2.298 +
   2.299          break;
   2.300      case SQLITE_DONE:
   2.301          // Got nothing.
   2.302 @@ -338,64 +432,214 @@
   2.303          break;
   2.304      default:
   2.305          ERROR_OUT(session, PEP_UNKNOWN_ERROR,
   2.306 -                  "stepping tsk_find_by_keyid: %s",
   2.307 -                  sqlite3_errmsg(session->key_db));
   2.308 +                  "stepping: %s", sqlite3_errmsg(session->key_db));
   2.309      }
   2.310  
   2.311   out:
   2.312 -    sqlite3_reset(stmt);
   2.313 -    T("%s -> %s", keyid_hex, pep_status_to_string(status));
   2.314 +    T(" -> %s", pep_status_to_string(status));
   2.315      return status;
   2.316  }
   2.317  
   2.318 -// See tsk_find_by_keyid_hex.
   2.319 -PEP_STATUS tsk_find_by_keyid(PEP_SESSION, sq_keyid_t, sq_tsk_t *)
   2.320 +// step statement until exhausted and load the tpks.
   2.321 +static PEP_STATUS key_loadn(PEP_SESSION, sqlite3_stmt *, sq_tpk_t **, int *)
   2.322 +    __attribute__((nonnull));
   2.323 +static PEP_STATUS key_loadn(PEP_SESSION session, sqlite3_stmt *stmt,
   2.324 +                            sq_tpk_t **tpksp, int *tpks_countp)
   2.325 +{
   2.326 +    PEP_STATUS status = PEP_STATUS_OK;
   2.327 +    int tpks_count = 0;
   2.328 +    int tpks_capacity = 8;
   2.329 +    sq_tpk_t *tpks = calloc(tpks_capacity, sizeof(sq_tpk_t));
   2.330 +    if (!tpks)
   2.331 +        ERROR_OUT(session, PEP_OUT_OF_MEMORY, "out of memory");
   2.332 +
   2.333 +    for (;;) {
   2.334 +        sq_tpk_t tpk = NULL;
   2.335 +        status = key_load(session, stmt, &tpk, NULL);
   2.336 +        if (status == PEP_KEY_NOT_FOUND) {
   2.337 +            status = PEP_STATUS_OK;
   2.338 +            break;
   2.339 +        }
   2.340 +        ERROR_OUT(session, status, "loading TPK");
   2.341 +
   2.342 +        if (tpks_count == tpks_capacity) {
   2.343 +            tpks_capacity *= 2;
   2.344 +            tpks = realloc(tpks, sizeof(tpks[0]) * tpks_capacity);
   2.345 +            if (!tpks)
   2.346 +                ERROR_OUT(session, PEP_OUT_OF_MEMORY, "tpks");
   2.347 +        }
   2.348 +        tpks[tpks_count ++] = tpk;
   2.349 +    }
   2.350 +
   2.351 + out:
   2.352 +    if (status != PEP_STATUS_OK) {
   2.353 +        for (int i = 0; i < tpks_count; i ++)
   2.354 +            sq_tpk_free(tpks[i]);
   2.355 +        free(tpks);
   2.356 +    } else {
   2.357 +        *tpksp = tpks;
   2.358 +        *tpks_countp = tpks_count;
   2.359 +    }
   2.360 +
   2.361 +    T(" -> %s (%d tpks)", pep_status_to_string(status), *tpks_countp);
   2.362 +    return status;
   2.363 +}
   2.364 +
   2.365 +// Returns the TPK identified by the provided fingerprint.
   2.366 +//
   2.367 +// This function only matches on the primary key!
   2.368 +static PEP_STATUS tpk_find(PEP_SESSION, sq_fingerprint_t, int, sq_tpk_t *, int *)
   2.369      __attribute__((nonnull(1, 2)));
   2.370 -PEP_STATUS tsk_find_by_keyid(
   2.371 -        PEP_SESSION session, sq_keyid_t keyid, sq_tsk_t *tsk)
   2.372 +static PEP_STATUS tpk_find(PEP_SESSION session,
   2.373 +                           sq_fingerprint_t fpr, int private_only,
   2.374 +                           sq_tpk_t *tpk, int *secret)
   2.375 +{
   2.376 +    PEP_STATUS status = PEP_STATUS_OK;
   2.377 +    char *fpr_str = sq_fingerprint_to_hex(fpr);
   2.378 +
   2.379 +    T("(%s, %d)", fpr_str, private_only);
   2.380 +
   2.381 +    sqlite3_stmt *stmt = private_only ? session->tsk_find : session->tpk_find;
   2.382 +    sqlite3_bind_text(stmt, 1, fpr_str, -1, SQLITE_STATIC);
   2.383 +
   2.384 +    status = key_load(session, stmt, tpk, secret);
   2.385 +    ERROR_OUT(session, status, "Looking up %s", fpr_str);
   2.386 +
   2.387 + out:
   2.388 +    sqlite3_reset(stmt);
   2.389 +    T("(%s, %d) -> %s", fpr_str, private_only, pep_status_to_string(status));
   2.390 +    free(fpr_str);
   2.391 +    return status;
   2.392 +}
   2.393 +
   2.394 +// Returns the TPK identified by the provided keyid.
   2.395 +//
   2.396 +// This function matches on both primary keys and subkeys!
   2.397 +//
   2.398 +// Note: There can be multiple TPKs for a given keyid.  This can
   2.399 +// occur, because an encryption subkey can be bound to multiple TPKs.
   2.400 +// Also, it is possible to collide key ids.  If there are multiple key
   2.401 +// ids for a given key, this just returns one of them.
   2.402 +//
   2.403 +// If private_only is set, this will only consider TPKs with some
   2.404 +// secret key material.
   2.405 +static PEP_STATUS tpk_find_by_keyid_hex(PEP_SESSION, const char *, int, sq_tpk_t *, int *)
   2.406 +  __attribute__((nonnull(1, 2)));
   2.407 +static PEP_STATUS tpk_find_by_keyid_hex(
   2.408 +        PEP_SESSION session, const char *keyid_hex, int private_only,
   2.409 +        sq_tpk_t *tpkp, int *secretp)
   2.410 +{
   2.411 +    PEP_STATUS status = PEP_STATUS_OK;
   2.412 +    T("(%s, %d)", keyid_hex, private_only);
   2.413 +
   2.414 +    sqlite3_stmt *stmt
   2.415 +        = private_only ? session->tsk_find_by_keyid : session->tpk_find_by_keyid;
   2.416 +    sqlite3_bind_text(stmt, 1, keyid_hex, -1, SQLITE_STATIC);
   2.417 +
   2.418 +    status = key_load(session, stmt, tpkp, secretp);
   2.419 +    ERROR_OUT(session, status, "Looking up %s", keyid_hex);
   2.420 +
   2.421 + out:
   2.422 +    sqlite3_reset(stmt);
   2.423 +    T("(%s, %d) -> %s", keyid_hex, private_only, pep_status_to_string(status));
   2.424 +    return status;
   2.425 +}
   2.426 +
   2.427 +// See tpk_find_by_keyid_hex.
   2.428 +PEP_STATUS tpk_find_by_keyid(PEP_SESSION, sq_keyid_t, int, sq_tpk_t *, int *)
   2.429 +    __attribute__((nonnull(1, 2)));
   2.430 +PEP_STATUS tpk_find_by_keyid(PEP_SESSION session,
   2.431 +                             sq_keyid_t keyid, int private_only,
   2.432 +                             sq_tpk_t *tpkp, int *secretp)
   2.433  {
   2.434      char *keyid_hex = sq_keyid_to_hex(keyid);
   2.435      if (! keyid_hex)
   2.436          return PEP_OUT_OF_MEMORY;
   2.437 -    PEP_STATUS status = tsk_find_by_keyid_hex(session, keyid_hex, tsk);
   2.438 +    PEP_STATUS status
   2.439 +        = tpk_find_by_keyid_hex(session, keyid_hex, private_only, tpkp, secretp);
   2.440      free(keyid_hex);
   2.441      return status;
   2.442  }
   2.443  
   2.444 -// See tsk_find_by_keyid_hex.
   2.445 -static PEP_STATUS tsk_find_by_fpr(PEP_SESSION, sq_fingerprint_t, sq_tsk_t *)
   2.446 +// See tpk_find_by_keyid_hex.
   2.447 +static PEP_STATUS tpk_find_by_fpr(PEP_SESSION, sq_fingerprint_t, int,
   2.448 +                                  sq_tpk_t *, int *)
   2.449      __attribute__((nonnull(1, 2)));
   2.450 -static PEP_STATUS tsk_find_by_fpr(
   2.451 -        PEP_SESSION session, sq_fingerprint_t fpr, sq_tsk_t *tsk)
   2.452 +static PEP_STATUS tpk_find_by_fpr(
   2.453 +    PEP_SESSION session, sq_fingerprint_t fpr, int private_only,
   2.454 +    sq_tpk_t *tpkp, int *secretp)
   2.455  {
   2.456      sq_keyid_t keyid = sq_fingerprint_to_keyid(fpr);
   2.457      if (! keyid)
   2.458          return PEP_OUT_OF_MEMORY;
   2.459 -    PEP_STATUS status = tsk_find_by_keyid(session, keyid, tsk);
   2.460 +    PEP_STATUS status
   2.461 +        = tpk_find_by_keyid(session, keyid, private_only, tpkp, secretp);
   2.462      sq_keyid_free(keyid);
   2.463      return status;
   2.464  }
   2.465  
   2.466 -// See tsk_find_by_keyid_hex.
   2.467 -static PEP_STATUS tsk_find_by_fpr_hex(PEP_SESSION, const char *, sq_tsk_t *)
   2.468 +// See tpk_find_by_keyid_hex.
   2.469 +static PEP_STATUS tpk_find_by_fpr_hex(PEP_SESSION, const char *, int, sq_tpk_t *, int *secret)
   2.470      __attribute__((nonnull(1, 2)));
   2.471 -static PEP_STATUS tsk_find_by_fpr_hex(
   2.472 -        PEP_SESSION session, const char *fpr, sq_tsk_t *tsk)
   2.473 +static PEP_STATUS tpk_find_by_fpr_hex(
   2.474 +    PEP_SESSION session, const char *fpr, int private_only,
   2.475 +    sq_tpk_t *tpkp, int *secretp)
   2.476  {
   2.477      sq_fingerprint_t sq_fpr = sq_fingerprint_from_hex(fpr);
   2.478      if (! sq_fpr)
   2.479          return PEP_OUT_OF_MEMORY;
   2.480 -    PEP_STATUS status = tsk_find_by_fpr(session, sq_fpr, tsk);
   2.481 +    PEP_STATUS status
   2.482 +        = tpk_find_by_fpr(session, sq_fpr, private_only, tpkp, secretp);
   2.483      sq_fingerprint_free(sq_fpr);
   2.484      return status;
   2.485  }
   2.486  
   2.487 +// Returns all known TPKs.
   2.488 +static PEP_STATUS tpk_all(PEP_SESSION, int, sq_tpk_t **, int *) __attribute__((nonnull));
   2.489 +static PEP_STATUS tpk_all(PEP_SESSION session, int private_only,
   2.490 +                          sq_tpk_t **tpksp, int *tpks_countp) {
   2.491 +    PEP_STATUS status = PEP_STATUS_OK;
   2.492 +    sqlite3_stmt *stmt = private_only ? session->tsk_all : session->tpk_all;
   2.493 +    status = key_loadn(session, stmt, tpksp, tpks_countp);
   2.494 +    ERROR_OUT(session, status, "loading TPKs");
   2.495 + out:
   2.496 +    sqlite3_reset(stmt);
   2.497 +    return status;
   2.498 +}
   2.499  
   2.500 -// Saves the specified TSK.
   2.501 +// Returns keys that have a user id that matches the specified pattern.
   2.502  //
   2.503 -// This function takes ownership of TSK.
   2.504 -static PEP_STATUS tsk_save(PEP_SESSION, sq_tsk_t) __attribute__((nonnull));
   2.505 -static PEP_STATUS tsk_save(PEP_SESSION session, sq_tsk_t tsk)
   2.506 +// The keys returned must be freed using sq_tpk_free.
   2.507 +static PEP_STATUS tpk_find_by_email(PEP_SESSION, const char *, int, sq_tpk_t **, int *)
   2.508 +    __attribute__((nonnull));
   2.509 +static PEP_STATUS tpk_find_by_email(PEP_SESSION session,
   2.510 +                                    const char *pattern, int private_only,
   2.511 +                                    sq_tpk_t **tpksp, int *countp)
   2.512 +{
   2.513 +    PEP_STATUS status = PEP_STATUS_OK;
   2.514 +    T("(%s)", pattern);
   2.515 +
   2.516 +    sqlite3_stmt *stmt
   2.517 +        = private_only ? session->tsk_find_by_email : session->tpk_find_by_email;
   2.518 +    sqlite3_bind_text(stmt, 1, pattern, -1, SQLITE_STATIC);
   2.519 +
   2.520 +    status = key_loadn(session, stmt, tpksp, countp);
   2.521 +    ERROR_OUT(session, status, "Searching for '%s'", pattern);
   2.522 +
   2.523 + out:
   2.524 +    sqlite3_reset(stmt);
   2.525 +    T("(%s) -> %s (%d results)", pattern, pep_status_to_string(status), *countp);
   2.526 +    return status;
   2.527 +}
   2.528 +
   2.529 +
   2.530 +// Saves the specified TPK.
   2.531 +//
   2.532 +// This function takes ownership of TPK.
   2.533 +static PEP_STATUS tpk_save(PEP_SESSION, sq_tpk_t, identity_list **)
   2.534 +    __attribute__((nonnull(1, 2)));
   2.535 +static PEP_STATUS tpk_save(PEP_SESSION session, sq_tpk_t tpk,
   2.536 +                           identity_list **private_idents)
   2.537  {
   2.538      PEP_STATUS status = PEP_STATUS_OK;
   2.539      sq_fingerprint_t sq_fpr = NULL;
   2.540 @@ -403,37 +647,37 @@
   2.541      void *tsk_buffer = NULL;
   2.542      size_t tsk_buffer_len = 0;
   2.543      int tried_commit = 0;
   2.544 -    sq_tpk_t tpk = sq_tsk_tpk(tsk); /* Reference. */
   2.545      sq_tpk_key_iter_t key_iter = NULL;
   2.546 +    sq_user_id_binding_iter_t user_id_iter = NULL;
   2.547  
   2.548      sq_fpr = sq_tpk_fingerprint(tpk);
   2.549      fpr = sq_fingerprint_to_hex(sq_fpr);
   2.550 -    T("%s", fpr);
   2.551 +    T("(%s, private_idents: %s)", fpr, private_idents ? "yes" : "no");
   2.552  
   2.553 -    // Merge any existing data into TSK.
   2.554 -    sq_tsk_t current = NULL;
   2.555 -    status = tsk_find_by_fpr(session, sq_fpr, &current);
   2.556 +    // Merge any existing data into TPK.
   2.557 +    sq_tpk_t current = NULL;
   2.558 +    status = tpk_find(session, sq_fpr, false, &current, NULL);
   2.559      if (status == PEP_KEY_NOT_FOUND)
   2.560          status = PEP_STATUS_OK;
   2.561      else
   2.562          ERROR_OUT(session, status, "Looking up %s", fpr);
   2.563 -    if (current) {
   2.564 -        tpk = sq_tpk_merge(session->ctx,
   2.565 -                           sq_tsk_into_tpk(tsk), sq_tsk_into_tpk(current));
   2.566 -        tsk = sq_tpk_into_tsk(tpk);
   2.567 -        tpk = sq_tsk_tpk(tsk);
   2.568 -    }
   2.569 +    if (current)
   2.570 +        tpk = sq_tpk_merge(session->ctx, tpk, current);
   2.571  
   2.572 +    int is_tsk = sq_tpk_is_tsk(tpk);
   2.573  
   2.574      // Serialize it.
   2.575      sq_writer_t writer = sq_writer_alloc(&tsk_buffer, &tsk_buffer_len);
   2.576      if (! writer)
   2.577          ERROR_OUT(session, PEP_OUT_OF_MEMORY, "out of memory");
   2.578  
   2.579 -    sq_status_t sq_status = sq_tsk_serialize(session->ctx, tsk, writer);
   2.580 +    sq_status_t sq_status;
   2.581 +    sq_tsk_t tsk = sq_tpk_into_tsk(tpk);
   2.582 +    sq_status = sq_tsk_serialize(session->ctx, tsk, writer);
   2.583 +    tpk = sq_tsk_into_tpk(tsk);
   2.584      //sq_writer_free(writer);
   2.585      if (sq_status != 0)
   2.586 -        ERROR_OUT(session, PEP_UNKNOWN_ERROR, "Serializing TSK");
   2.587 +        ERROR_OUT(session, PEP_UNKNOWN_ERROR, "Serializing TPK");
   2.588  
   2.589  
   2.590      // Insert the TSK into the DB.
   2.591 @@ -445,25 +689,24 @@
   2.592                    "begin transaction failed: %s",
   2.593                    sqlite3_errmsg(session->key_db));
   2.594  
   2.595 -    stmt = session->tsk_save_insert_primary;
   2.596 +    stmt = session->tpk_save_insert_primary;
   2.597      sqlite3_bind_text(stmt, 1, fpr, -1, SQLITE_STATIC);
   2.598 -    sqlite3_bind_blob(stmt, 2, tsk_buffer, tsk_buffer_len, SQLITE_STATIC);
   2.599 +    sqlite3_bind_int(stmt, 2, is_tsk);
   2.600 +    sqlite3_bind_blob(stmt, 3, tsk_buffer, tsk_buffer_len, SQLITE_STATIC);
   2.601  
   2.602      sqlite_result = sqlite3_step(stmt);
   2.603      sqlite3_reset(stmt);
   2.604      if (sqlite_result != SQLITE_DONE)
   2.605          ERROR_OUT(session, PEP_UNKNOWN_ERROR,
   2.606 -                  "Saving TSK to DB: %s",
   2.607 -                  sqlite3_errmsg(session->key_db));
   2.608 +                  "Saving TPK: %s", sqlite3_errmsg(session->key_db));
   2.609  
   2.610      // Insert the "subkeys" (the primary key and the subkeys).
   2.611 -    stmt = session->tsk_save_insert_subkeys;
   2.612 +    stmt = session->tpk_save_insert_subkeys;
   2.613      key_iter = sq_tpk_key_iter(tpk);
   2.614      sq_p_key_t key;
   2.615      while ((key = sq_tpk_key_iter_next(key_iter, NULL, NULL))) {
   2.616          sq_keyid_t keyid = sq_p_key_keyid(key);
   2.617          char *keyid_hex = sq_keyid_to_hex(keyid);
   2.618 -        T("  subkey: %s", keyid_hex);
   2.619          sqlite3_bind_text(stmt, 1, keyid_hex, -1, SQLITE_STATIC);
   2.620          sqlite3_bind_text(stmt, 2, fpr, -1, SQLITE_STATIC);
   2.621  
   2.622 @@ -480,6 +723,63 @@
   2.623      sq_tpk_key_iter_free(key_iter);
   2.624      key_iter = NULL;
   2.625  
   2.626 +    // Insert the "userids".
   2.627 +    stmt = session->tpk_save_insert_userids;
   2.628 +    user_id_iter = sq_tpk_user_id_binding_iter(tpk);
   2.629 +    sq_user_id_binding_t binding;
   2.630 +    int first = 1;
   2.631 +    while ((binding = sq_user_id_binding_iter_next(user_id_iter))) {
   2.632 +        char *user_id = sq_user_id_binding_user_id(binding);
   2.633 +        if (!user_id || !*user_id)
   2.634 +            continue;
   2.635 +
   2.636 +        // Ignore bindings with a self-revocation certificate, but no
   2.637 +        // self-signature.
   2.638 +        if (!sq_user_id_binding_selfsig(binding)) {
   2.639 +            free(user_id);
   2.640 +            continue;
   2.641 +        }
   2.642 +
   2.643 +        char *name, *email;
   2.644 +        user_id_split(user_id, &name, &email); /* user_id is comsumed.  */
   2.645 +        // XXX: Correctly clean up name and email on error...
   2.646 +
   2.647 +        if (email) {
   2.648 +            T("  userid: %s", email);
   2.649 +
   2.650 +            sqlite3_bind_text(stmt, 1, email, -1, SQLITE_STATIC);
   2.651 +            sqlite3_bind_text(stmt, 2, fpr, -1, SQLITE_STATIC);
   2.652 +
   2.653 +            sqlite_result = sqlite3_step(stmt);
   2.654 +            sqlite3_reset(stmt);
   2.655 +
   2.656 +            if (sqlite_result != SQLITE_DONE) {
   2.657 +                sq_user_id_binding_iter_free(user_id_iter);
   2.658 +                free(name);
   2.659 +                ERROR_OUT(session, PEP_UNKNOWN_ERROR,
   2.660 +                          "Updating userids: %s", sqlite3_errmsg(session->key_db));
   2.661 +            }
   2.662 +        }
   2.663 +
   2.664 +        if (first && private_idents && is_tsk) {
   2.665 +            first = 0;
   2.666 +
   2.667 +            // Create an identity for the primary user id.
   2.668 +            pEp_identity *ident = new_identity(email, fpr, NULL, name);
   2.669 +            if (ident == NULL)
   2.670 +                ERROR_OUT(session, PEP_OUT_OF_MEMORY, "new_identity");
   2.671 +
   2.672 +            *private_idents = identity_list_add(*private_idents, ident);
   2.673 +            if (*private_idents == NULL)
   2.674 +                ERROR_OUT(session, PEP_OUT_OF_MEMORY, "identity_list_add");
   2.675 +        }
   2.676 +        free(email);
   2.677 +        free(name);
   2.678 +
   2.679 +    }
   2.680 +    sq_user_id_binding_iter_free(user_id_iter);
   2.681 +    user_id_iter = NULL;
   2.682 +
   2.683   out:
   2.684      // Prevent ERROR_OUT from causing an infinite loop.
   2.685      if (! tried_commit) {
   2.686 @@ -497,314 +797,21 @@
   2.687  
   2.688      T("(%s) -> %s", fpr, pep_status_to_string(status));
   2.689  
   2.690 +    if (user_id_iter)
   2.691 +        sq_user_id_binding_iter_free(user_id_iter);
   2.692      if (key_iter)
   2.693          sq_tpk_key_iter_free(key_iter);
   2.694      if (stmt)
   2.695        sqlite3_reset(stmt);
   2.696      free(tsk_buffer);
   2.697 -    sq_tsk_free(tsk);
   2.698 +    if (tpk)
   2.699 +        sq_tpk_free(tpk);
   2.700      free(fpr);
   2.701      sq_fingerprint_free(sq_fpr);
   2.702  
   2.703      return status;
   2.704  }
   2.705  
   2.706 -// Returns all known TSKs.
   2.707 -static PEP_STATUS tsk_all(PEP_SESSION, sq_tsk_t **, int *) __attribute__((nonnull));
   2.708 -static PEP_STATUS tsk_all(PEP_SESSION session, sq_tsk_t **tsksp, int *tsks_countp) {
   2.709 -    PEP_STATUS status = PEP_STATUS_OK;
   2.710 -
   2.711 -    int tsks_count = 0;
   2.712 -    int tsks_capacity = 8;
   2.713 -    sq_tsk_t *tsks = calloc(tsks_capacity, sizeof(sq_tsk_t));
   2.714 -    if (!tsks)
   2.715 -        ERROR_OUT(session, PEP_OUT_OF_MEMORY, "out of memory");
   2.716 -
   2.717 -    sqlite3_stmt *stmt = session->tsk_all;
   2.718 -    while (true) {
   2.719 -        switch (sqlite3_step(stmt)) {
   2.720 -        case SQLITE_ROW: {
   2.721 -            int data_len = sqlite3_column_bytes(stmt, 0);
   2.722 -            const void *data = sqlite3_column_blob(stmt, 0);
   2.723 -            sq_tpk_t tpk = sq_tpk_from_bytes(session->ctx, data, data_len);
   2.724 -            if (!tpk) {
   2.725 -                ERROR_OUT(session, PEP_GET_KEY_FAILED, "parsing TSK");
   2.726 -            } else {
   2.727 -                if (tsks_count == tsks_capacity) {
   2.728 -                    tsks_capacity *= 2;
   2.729 -                    tsks = realloc(tsks, sizeof(tsks[0]) * tsks_capacity);
   2.730 -                    if (!tsks)
   2.731 -                        ERROR_OUT(session, PEP_OUT_OF_MEMORY, "tsks");
   2.732 -                }
   2.733 -                tsks[tsks_count ++] = sq_tpk_into_tsk(tpk);
   2.734 -            }
   2.735 -            break;
   2.736 -        }
   2.737 -        default:
   2.738 -            ERROR_OUT(session, PEP_UNKNOWN_ERROR,
   2.739 -                      "stepping sqlite statement: %s",
   2.740 -                      sqlite3_errmsg(session->key_db));
   2.741 -        case SQLITE_DONE:
   2.742 -            goto out;
   2.743 -        }
   2.744 -    }
   2.745 -
   2.746 - out:
   2.747 -    sqlite3_reset(stmt);
   2.748 -
   2.749 -    if (status != PEP_STATUS_OK) {
   2.750 -        for (int i = 0; i < tsks_count; i ++)
   2.751 -            sq_tsk_free(tsks[i]);
   2.752 -        free(tsks);
   2.753 -    } else {
   2.754 -        *tsksp = tsks;
   2.755 -        *tsks_countp = tsks_count;
   2.756 -    }
   2.757 -
   2.758 -    return status;
   2.759 -}
   2.760 -
   2.761 -// Returns the key with the label LABEL.
   2.762 -//
   2.763 -// The return is returned in *KEY and must be freed using sq_tpk_free.
   2.764 -//
   2.765 -// Note: we maintain labels for the fingerprint of primary keys, pep
   2.766 -// user ids, and email addresses.  If you want to look something up by
   2.767 -// subkey id, use tpk_find_by_keyid.
   2.768 -static PEP_STATUS tpk_find_by_label(PEP_SESSION, const char *, sq_tpk_t *)
   2.769 -    __attribute__((nonnull));
   2.770 -static PEP_STATUS tpk_find_by_label(PEP_SESSION session, const char *label, sq_tpk_t *tpk)
   2.771 -{
   2.772 -    PEP_STATUS status = PEP_STATUS_OK;
   2.773 -
   2.774 -    sq_binding_t binding
   2.775 -        = sq_store_lookup(session->ctx, session->store, label);
   2.776 -    if (!binding)
   2.777 -        ERROR_OUT(session, PEP_KEY_NOT_FOUND, "looking up label %s", label);
   2.778 -
   2.779 -    *tpk = sq_binding_tpk(session->ctx, binding);
   2.780 -    if (!*tpk)
   2.781 -        ERROR_OUT(session, PEP_GET_KEY_FAILED, "getting TPK");
   2.782 -
   2.783 - out:
   2.784 -    if (binding)
   2.785 -        sq_binding_free(binding);
   2.786 -
   2.787 -    return status;
   2.788 -}
   2.789 -
   2.790 -// Returns the key with keyid KEYID.
   2.791 -//
   2.792 -// Note: this function will match both the primary key as well as any
   2.793 -// subkeys.
   2.794 -static PEP_STATUS tpk_find_by_keyid(PEP_SESSION, sq_keyid_t, sq_tpk_t *)
   2.795 -    __attribute__((nonnull));
   2.796 -static PEP_STATUS tpk_find_by_keyid(PEP_SESSION session, sq_keyid_t keyid,
   2.797 -                                    sq_tpk_t *tpk)
   2.798 -{
   2.799 -    PEP_STATUS status = PEP_STATUS_OK;
   2.800 -    char *keyid_hex = sq_keyid_to_hex(keyid);
   2.801 -
   2.802 -    sq_key_t key = sq_store_lookup_by_subkeyid(session->ctx, keyid);
   2.803 -    if (!key)
   2.804 -        ERROR_OUT(session, PEP_KEY_NOT_FOUND,
   2.805 -                  "looking up key by keyid %s", keyid_hex);
   2.806 -
   2.807 -    *tpk = sq_key_tpk(session->ctx, key);
   2.808 -    if (!*tpk)
   2.809 -        ERROR_OUT(session, PEP_GET_KEY_FAILED, "getting TPK");
   2.810 -
   2.811 - out:
   2.812 -    free(keyid_hex);
   2.813 -
   2.814 -    return status;
   2.815 -}
   2.816 -
   2.817 -// Returns the key with fingerprint FPR.
   2.818 -//
   2.819 -// Note: this function will match both the primary key as well as any
   2.820 -// subkeys.
   2.821 -static PEP_STATUS tpk_find_by_fpr(PEP_SESSION, sq_fingerprint_t, sq_tpk_t *)
   2.822 -    __attribute__((nonnull));
   2.823 -static PEP_STATUS tpk_find_by_fpr(PEP_SESSION session, sq_fingerprint_t fpr,
   2.824 -                                  sq_tpk_t *tpk)
   2.825 -{
   2.826 -    sq_keyid_t keyid = sq_fingerprint_to_keyid(fpr);
   2.827 -    PEP_STATUS status = tpk_find_by_keyid(session, keyid, tpk);
   2.828 -    sq_keyid_free(keyid);
   2.829 -    return status;
   2.830 -}
   2.831 -
   2.832 -
   2.833 -
   2.834 -// Saves a TPK.
   2.835 -//
   2.836 -// Creates labels under the fingerprint, address (if not NULL), and
   2.837 -// the email address in each user id.
   2.838 -//
   2.839 -// If there are any keys with private key material, saves that
   2.840 -// information in private_idents (if not NULL).
   2.841 -//
   2.842 -// This function takes ownership of the tpk.
   2.843 -static PEP_STATUS tpk_save(PEP_SESSION, sq_tpk_t, const char *,
   2.844 -                           identity_list **, int)
   2.845 -  __attribute__((nonnull(1, 2)));
   2.846 -static PEP_STATUS tpk_save(PEP_SESSION session, sq_tpk_t tpk,
   2.847 -                           const char *address, identity_list **private_idents,
   2.848 -                           int replace_bindings)
   2.849 -{
   2.850 -    PEP_STATUS status = PEP_STATUS_OK;
   2.851 -    sq_user_id_binding_iter_t iter = NULL;
   2.852 -    sq_user_id_binding_t user_id_binding = NULL;
   2.853 -
   2.854 -    if (private_idents)
   2.855 -        *private_idents = NULL;
   2.856 -
   2.857 -    sq_fingerprint_t sq_fpr = sq_tpk_fingerprint(tpk);
   2.858 -    char *fpr = sq_fingerprint_to_hex(sq_fpr);
   2.859 -    T("(%s)", fpr);
   2.860 -
   2.861 -    // Import the public part in the store.  If it was already present
   2.862 -    // in the store, it will be merged.  We don't work with the merged
   2.863 -    // TPK, because we only care about new user ids.
   2.864 -    sq_tpk_t merged = sq_store_import(session->ctx, session->store, fpr, tpk);
   2.865 -    if (! merged)
   2.866 -        ERROR_OUT(session, PEP_UNKNOWN_ERROR, "Merging TPK (%s)", fpr);
   2.867 -    sq_tpk_free(merged);
   2.868 -
   2.869 -    // Add the pep user id label.
   2.870 -    if (address) {
   2.871 -        int first_try = 1;
   2.872 -        sq_binding_t binding;
   2.873 -
   2.874 -    make_address_binding:
   2.875 -        binding = sq_store_add(session->ctx, session->store, address, sq_fpr);
   2.876 -        if (! binding) {
   2.877 -            // An error occured.  There's a good chance this is
   2.878 -            // because the binding already exists.
   2.879 -            if (replace_bindings && first_try) {
   2.880 -                T("replacing userid binding %s -> %s", address, fpr);
   2.881 -
   2.882 -                // We should replace the existing binding.
   2.883 -                binding = sq_store_lookup(session->ctx, session->store, address);
   2.884 -                if (binding) {
   2.885 -                    if (sq_binding_delete(session->ctx, binding)) {
   2.886 -                        DUMP_ERR(session, PEP_STATUS_OK,
   2.887 -                                 "Delete binding %s", address);
   2.888 -                        sq_binding_free(binding);
   2.889 -                    }
   2.890 -
   2.891 -                    first_try = 0;
   2.892 -                    goto make_address_binding;
   2.893 -                }
   2.894 -            }
   2.895 -
   2.896 -            // This is a soft error: we always prefer the existing
   2.897 -            // binding.
   2.898 -            DUMP_ERR(session, PEP_STATUS_OK,
   2.899 -                     "Creating userid binding %s -> %s", address, fpr);
   2.900 -        } else {
   2.901 -            sq_binding_free(binding);
   2.902 -        }
   2.903 -    }
   2.904 -
   2.905 -    // Create a binding for each user id.
   2.906 -    //
   2.907 -    // Note: the iterator only returns valid user ids in the sense
   2.908 -    // that the user id has a self-signature or a self-revocation
   2.909 -    // certificate.
   2.910 -    int first = 1;
   2.911 -    iter = sq_tpk_user_id_binding_iter(tpk);
   2.912 -    while ((user_id_binding = sq_user_id_binding_iter_next(iter))) {
   2.913 -        char *user_id = sq_user_id_binding_user_id(user_id_binding);
   2.914 -        if (!user_id) {
   2.915 -            // Completely ignore insane user ids (those with interior
   2.916 -            // NUL bytes).
   2.917 -            free(user_id);
   2.918 -            continue;
   2.919 -        }
   2.920 -
   2.921 -        // Ignore bindings with a self-revocation certificate, but no
   2.922 -        // self-signature.
   2.923 -        if (!sq_user_id_binding_selfsig(user_id_binding)) {
   2.924 -            free(user_id);
   2.925 -            continue;
   2.926 -        }
   2.927 -
   2.928 -        char *name, *email;
   2.929 -        user_id_split(user_id, &name, &email); /* user_id is comsumed.  */
   2.930 -
   2.931 -        if (email) {
   2.932 -            int first_try = 1;
   2.933 -            sq_binding_t binding;
   2.934 -
   2.935 -        make_email_binding:
   2.936 -            binding = sq_store_add(session->ctx, session->store, email, sq_fpr);
   2.937 -            if (! binding) {
   2.938 -                // An error occured.  There's a good chance this is
   2.939 -                // because the binding already exists.
   2.940 -                if (replace_bindings && first_try) {
   2.941 -                    // We should replace the existing binding.
   2.942 -                    binding = sq_store_lookup(session->ctx, session->store, email);
   2.943 -                    if (binding) {
   2.944 -                        if (sq_binding_delete(session->ctx, binding)) {
   2.945 -                            DUMP_ERR(session, PEP_STATUS_OK,
   2.946 -                                     "Delete binding %s", email);
   2.947 -                            sq_binding_free(binding);
   2.948 -                        }
   2.949 -
   2.950 -                        first_try = 0;
   2.951 -                        goto make_email_binding;
   2.952 -                    }
   2.953 -                }
   2.954 -
   2.955 -                // This is a soft error: we always prefer the existing
   2.956 -                // binding.
   2.957 -                DUMP_ERR(session, PEP_UNKNOWN_ERROR,
   2.958 -                         "Creating email binding: %s -> %s", email, fpr);
   2.959 -            } else {
   2.960 -                sq_binding_free(binding);
   2.961 -            }
   2.962 -
   2.963 -            if (first && private_idents && sq_tpk_is_tsk(tpk)) {
   2.964 -                first = 0;
   2.965 -
   2.966 -                // Create an identity for the primary user id.
   2.967 -                pEp_identity *ident = new_identity(email, fpr, NULL, name);
   2.968 -                if (ident == NULL)
   2.969 -                    ERROR_OUT(session, PEP_OUT_OF_MEMORY, "new_identity");
   2.970 -
   2.971 -                *private_idents = identity_list_add(*private_idents, ident);
   2.972 -                if (*private_idents == NULL)
   2.973 -                    ERROR_OUT(session, PEP_OUT_OF_MEMORY, "identity_list_add");
   2.974 -            }
   2.975 -        }
   2.976 -    }
   2.977 -
   2.978 -    sq_user_id_binding_iter_free(iter);
   2.979 -    iter = NULL;
   2.980 -
   2.981 -    // If it has any private key material, save it in the TSK store.
   2.982 -    if (sq_tpk_is_tsk(tpk)) {
   2.983 -        status = tsk_save(session, sq_tpk_into_tsk(tpk));
   2.984 -        tpk = NULL;
   2.985 -        ERROR_OUT(session, status, "Saving TSK");
   2.986 -    }
   2.987 -
   2.988 - out:
   2.989 -    T("(%s) -> %s", fpr, pep_status_to_string(status));
   2.990 -
   2.991 -    if (iter)
   2.992 -        sq_user_id_binding_iter_free(iter);
   2.993 -    free(fpr);
   2.994 -    if (sq_fpr)
   2.995 -        sq_fingerprint_free(sq_fpr);
   2.996 -    if (tpk)
   2.997 -        sq_tpk_free(tpk);
   2.998 -
   2.999 -    return status;
  2.1000 -}
  2.1001 -
  2.1002  struct decrypt_cookie {
  2.1003      PEP_SESSION session;
  2.1004      int get_secret_keys_called;
  2.1005 @@ -834,7 +841,8 @@
  2.1006      j = 0;
  2.1007      for (i = 0; i < keyids_len; i ++) {
  2.1008          sq_tpk_t tpk = NULL;
  2.1009 -        sq_status_t status = tpk_find_by_keyid(session, keyids[i], &tpk);
  2.1010 +        sq_status_t status
  2.1011 +            = tpk_find_by_keyid(session, keyids[i], false, &tpk, NULL);
  2.1012          if (status == SQ_STATUS_SUCCESS)
  2.1013              (*tpks)[j ++] = tpk;
  2.1014      }
  2.1015 @@ -850,7 +858,7 @@
  2.1016  {
  2.1017      struct decrypt_cookie *cookie = cookie_opaque;
  2.1018      PEP_SESSION session = cookie->session;
  2.1019 -    sq_tsk_t *tsks = NULL;
  2.1020 +    sq_tpk_t *tsks = NULL;
  2.1021      int tsks_count = 0;
  2.1022      int wildcards = 0;
  2.1023  
  2.1024 @@ -879,30 +887,24 @@
  2.1025          // Collect the recipients.  Note: we must return the primary
  2.1026          // key's fingerprint.
  2.1027          sq_tpk_t tpk = NULL;
  2.1028 -        if (tpk_find_by_keyid(session, keyid, &tpk) == PEP_STATUS_OK) {
  2.1029 -            sq_fingerprint_t fp = sq_tpk_fingerprint(tpk);
  2.1030 -            char *fp_string = sq_fingerprint_to_hex(fp);
  2.1031 -            stringlist_add_unique(cookie->recipient_keylist, fp_string);
  2.1032 -            free(fp_string);
  2.1033 -            sq_fingerprint_free(fp);
  2.1034 -            sq_tpk_free(tpk);
  2.1035 -        }
  2.1036 +        int is_tsk = 0;
  2.1037 +        if (tpk_find_by_keyid(session, keyid, false, &tpk, &is_tsk) != PEP_STATUS_OK)
  2.1038 +            goto eol;
  2.1039 +
  2.1040 +        sq_fingerprint_t fp = sq_tpk_fingerprint(tpk);
  2.1041 +        char *fp_string = sq_fingerprint_to_hex(fp);
  2.1042 +        stringlist_add_unique(cookie->recipient_keylist, fp_string);
  2.1043 +        free(fp_string);
  2.1044 +        sq_fingerprint_free(fp);
  2.1045  
  2.1046          if (cookie->decrypted)
  2.1047              goto eol;
  2.1048  
  2.1049          // See if we have the secret key.
  2.1050 -        sq_tsk_t tsk = NULL;
  2.1051 -        PEP_STATUS s = tsk_find_by_keyid(cookie->session, keyid, &tsk);
  2.1052 -        if (s != PEP_STATUS_OK) {
  2.1053 -            if (s != PEP_KEY_NOT_FOUND)
  2.1054 -                DUMP_ERR(cookie->session, s, "Parsing key %s", keyid_str);
  2.1055 -            else
  2.1056 -                T("No secret key material for %s", keyid_str);
  2.1057 +        assert(is_tsk == sq_tpk_is_tsk(tpk));
  2.1058 +        if (! is_tsk)
  2.1059              goto eol;
  2.1060 -        }
  2.1061  
  2.1062 -        tpk = sq_tsk_tpk(tsk);
  2.1063          key_iter = sq_tpk_key_iter(tpk);
  2.1064          sq_p_key_t key;
  2.1065          while ((key = sq_tpk_key_iter_next(key_iter, NULL, NULL))) {
  2.1066 @@ -916,8 +918,10 @@
  2.1067                  break;
  2.1068          }
  2.1069  
  2.1070 -        if (key == NULL)
  2.1071 +        if (key == NULL) {
  2.1072              assert(!"Inconsistent DB: key doesn't contain a subkey with keyid!");
  2.1073 +            goto eol;
  2.1074 +        }
  2.1075  
  2.1076          uint8_t algo;
  2.1077          uint8_t session_key[1024];
  2.1078 @@ -938,6 +942,8 @@
  2.1079          free(keyid_str);
  2.1080          if (key_iter)
  2.1081              sq_tpk_key_iter_free(key_iter);
  2.1082 +        if (tpk)
  2.1083 +            sq_tpk_free(tpk);
  2.1084      }
  2.1085  
  2.1086      // Consider wildcard recipients.
  2.1087 @@ -951,15 +957,15 @@
  2.1088              goto eol2;
  2.1089  
  2.1090          if (!tsks) {
  2.1091 -            if (tsk_all(session, &tsks, &tsks_count) != PEP_STATUS_OK) {
  2.1092 +            if (tpk_all(session, true, &tsks, &tsks_count) != PEP_STATUS_OK) {
  2.1093                  DUMP_ERR(session, PEP_UNKNOWN_ERROR, "Getting all tsks");
  2.1094              }
  2.1095          }
  2.1096  
  2.1097          for (int j = 0; j < tsks_count; j ++) {
  2.1098 -            sq_tpk_t tpk = sq_tsk_tpk(tsks[j]);
  2.1099 +            sq_tpk_t tsk = tsks[j];
  2.1100  
  2.1101 -            key_iter = sq_tpk_key_iter(tpk);
  2.1102 +            key_iter = sq_tpk_key_iter(tsk);
  2.1103              sq_p_key_t key;
  2.1104              sq_signature_t selfsig;
  2.1105              while ((key = sq_tpk_key_iter_next(key_iter, &selfsig, NULL))) {
  2.1106 @@ -979,7 +985,7 @@
  2.1107                      continue;
  2.1108  
  2.1109                  // Add it to the recipient list.
  2.1110 -                sq_fingerprint_t fp = sq_tpk_fingerprint(tpk);
  2.1111 +                sq_fingerprint_t fp = sq_tpk_fingerprint(tsk);
  2.1112                  char *fp_string = sq_fingerprint_to_hex(fp);
  2.1113                  T("wildcard recipient appears to be %s", fp_string);
  2.1114                  stringlist_add_unique(cookie->recipient_keylist, fp_string);
  2.1115 @@ -1001,7 +1007,7 @@
  2.1116  
  2.1117      if (tsks) {
  2.1118          for (int i = 0; i < tsks_count; i ++)
  2.1119 -            sq_tsk_free(tsks[i]);
  2.1120 +            sq_tpk_free(tsks[i]);
  2.1121          free(tsks);
  2.1122      }
  2.1123  
  2.1124 @@ -1046,7 +1052,7 @@
  2.1125              sq_fingerprint_t issuer_fp = sq_signature_issuer_fingerprint(sig);
  2.1126              if (issuer_fp) {
  2.1127                  sq_keyid_t issuer = sq_fingerprint_to_keyid(issuer_fp);
  2.1128 -                if (tpk_find_by_keyid(session, issuer, &tpk) != PEP_STATUS_OK)
  2.1129 +                if (tpk_find_by_keyid(session, issuer, false, &tpk, NULL) != PEP_STATUS_OK)
  2.1130                      ; // Soft error.  Ignore.
  2.1131                  sq_keyid_free(issuer);
  2.1132                  sq_fingerprint_free(issuer_fp);
  2.1133 @@ -1056,7 +1062,7 @@
  2.1134              if (!tpk) {
  2.1135                  sq_keyid_t issuer = sq_signature_issuer(sig);
  2.1136                  if (issuer) {
  2.1137 -                    if (tpk_find_by_keyid(session, issuer, &tpk) != PEP_STATUS_OK)
  2.1138 +                    if (tpk_find_by_keyid(session, issuer, false, &tpk, NULL) != PEP_STATUS_OK)
  2.1139                          ; // Soft error.  Ignore.
  2.1140                  }
  2.1141                  sq_keyid_free(issuer);
  2.1142 @@ -1268,13 +1274,11 @@
  2.1143      assert(ssize);
  2.1144  
  2.1145      PEP_STATUS status = PEP_STATUS_OK;
  2.1146 -    sq_tsk_t signer = NULL;
  2.1147 -    sq_tpk_t signer_tpk = NULL; /* Reference.  */
  2.1148 +    sq_tpk_t signer = NULL;
  2.1149      sq_writer_stack_t ws = NULL;
  2.1150  
  2.1151 -    status = tsk_find_by_fpr_hex(session, fpr, &signer);
  2.1152 +    status = tpk_find_by_fpr_hex(session, fpr, true, &signer, NULL);
  2.1153      ERROR_OUT(session, status, "Looking up key '%s'", fpr);
  2.1154 -    signer_tpk = sq_tsk_tpk(signer);
  2.1155  
  2.1156      sq_writer_t writer = sq_writer_alloc((void **) stext, ssize);
  2.1157      writer = sq_armor_writer_new(session->ctx, writer,
  2.1158 @@ -1284,7 +1288,7 @@
  2.1159  
  2.1160      ws = sq_writer_stack_message(writer);
  2.1161  
  2.1162 -    ws = sq_signer_new_detached(session->ctx, ws, &signer_tpk, 1);
  2.1163 +    ws = sq_signer_new_detached(session->ctx, ws, &signer, 1);
  2.1164      if (!ws)
  2.1165          ERROR_OUT(session, PEP_UNKNOWN_ERROR, "Setting up signer");
  2.1166  
  2.1167 @@ -1310,7 +1314,7 @@
  2.1168      }
  2.1169  
  2.1170      if (signer)
  2.1171 -        sq_tsk_free(signer);
  2.1172 +        sq_tpk_free(signer);
  2.1173  
  2.1174      T("(%s)-> %s", fpr, pep_status_to_string(status));
  2.1175      return status;
  2.1176 @@ -1323,8 +1327,7 @@
  2.1177      PEP_STATUS status = PEP_STATUS_OK;
  2.1178      int keys_count = 0;
  2.1179      sq_tpk_t *keys = NULL;
  2.1180 -    sq_tsk_t signer = NULL;
  2.1181 -    sq_tpk_t signer_tpk = NULL; /* Reference. */
  2.1182 +    sq_tpk_t signer = NULL;
  2.1183      sq_writer_stack_t ws = NULL;
  2.1184  
  2.1185      assert(session);
  2.1186 @@ -1346,16 +1349,15 @@
  2.1187      for (_keylist = keylist; _keylist != NULL; _keylist = _keylist->next) {
  2.1188          assert(_keylist->value);
  2.1189          sq_fingerprint_t sq_fpr = sq_fingerprint_from_hex(_keylist->value);
  2.1190 -        status = tpk_find_by_fpr(session, sq_fpr, &keys[keys_count ++]);
  2.1191 +        status = tpk_find_by_fpr(session, sq_fpr, false, &keys[keys_count ++], NULL);
  2.1192          sq_fingerprint_free(sq_fpr);
  2.1193 -        ERROR_OUT(session, status, "Looking up key '%s'", _keylist->value);
  2.1194 +        ERROR_OUT(session, status, "Looking up key for recipient '%s'", _keylist->value);
  2.1195      }
  2.1196  
  2.1197      if (sign) {
  2.1198          // The first key in the keylist is the signer.
  2.1199 -        status = tsk_find_by_fpr_hex(session, keylist->value, &signer);
  2.1200 -        ERROR_OUT(session, status, "Looking up key '%s'", keylist->value);
  2.1201 -        signer_tpk = sq_tsk_tpk(signer);
  2.1202 +        status = tpk_find_by_fpr_hex(session, keylist->value, true, &signer, NULL);
  2.1203 +        ERROR_OUT(session, status, "Looking up key for signing '%s'", keylist->value);
  2.1204      }
  2.1205  
  2.1206      sq_writer_t writer = sq_writer_alloc((void **) ctext, csize);
  2.1207 @@ -1374,7 +1376,7 @@
  2.1208      }
  2.1209  
  2.1210      if (sign) {
  2.1211 -        ws = sq_signer_new(session->ctx, ws, &signer_tpk, 1);
  2.1212 +        ws = sq_signer_new(session->ctx, ws, &signer, 1);
  2.1213          if (!ws)
  2.1214              ERROR_OUT(session, PEP_UNKNOWN_ERROR, "Setting up signer");
  2.1215      }
  2.1216 @@ -1405,7 +1407,7 @@
  2.1217      }
  2.1218  
  2.1219      if (signer)
  2.1220 -        sq_tsk_free(signer);
  2.1221 +        sq_tpk_free(signer);
  2.1222      for (int i = 0; i < keys_count; i ++)
  2.1223          sq_tpk_free(keys[i]);
  2.1224      free(keys);
  2.1225 @@ -1449,6 +1451,8 @@
  2.1226      if (! userid)
  2.1227          ERROR_OUT(session, PEP_OUT_OF_MEMORY, "asprintf");
  2.1228  
  2.1229 +    T("(%s)", userid);
  2.1230 +
  2.1231      // Generate a key.
  2.1232      sq_tsk_t tsk;
  2.1233      sq_signature_t rev;
  2.1234 @@ -1464,7 +1468,7 @@
  2.1235      sq_fpr = sq_tpk_fingerprint(tpk);
  2.1236      fpr = sq_fingerprint_to_hex(sq_fpr);
  2.1237  
  2.1238 -    status = tpk_save(session, tpk, identity->address, NULL, 1);
  2.1239 +    status = tpk_save(session, tpk, NULL);
  2.1240      tpk = NULL;
  2.1241      if (status != 0)
  2.1242          ERROR_OUT(session, PEP_CANNOT_CREATE_KEY, "saving TSK");
  2.1243 @@ -1534,7 +1538,7 @@
  2.1244  
  2.1245          // If private_idents is not NULL and there is any private key
  2.1246          // material, it will be saved.
  2.1247 -        status = tpk_save(session, tpk, NULL, private_idents, false);
  2.1248 +        status = tpk_save(session, tpk, private_idents);
  2.1249          ERROR_OUT(session, status, "saving TPK");
  2.1250  
  2.1251          break;
  2.1252 @@ -1569,19 +1573,14 @@
  2.1253      T("(%s, %s)", fpr, secret ? "secret" : "public");
  2.1254  
  2.1255      if (secret) {
  2.1256 -        sq_tsk_t tsk;
  2.1257 -        status = tsk_find_by_fpr_hex(session, fpr, &tsk);
  2.1258 -        if (status == PEP_KEY_NOT_FOUND) {
  2.1259 +        status = tpk_find_by_fpr_hex(session, fpr, true, &secret_key, NULL);
  2.1260 +        if (status == PEP_KEY_NOT_FOUND)
  2.1261              status = PEP_STATUS_OK;
  2.1262 -        } else if (status == PEP_STATUS_OK) {
  2.1263 -            secret_key = sq_tsk_into_tpk(tsk);
  2.1264 -        } else {
  2.1265 -            ERROR_OUT(session, status, "Looking up TSK");
  2.1266 -        }
  2.1267 +        ERROR_OUT(session, status, "Looking up TSK for %s", fpr);
  2.1268      }
  2.1269  
  2.1270      sq_fingerprint_t sq_fpr = sq_fingerprint_from_hex(fpr);
  2.1271 -    status = tpk_find_by_fpr(session, sq_fpr, &tpk);
  2.1272 +    status = tpk_find_by_fpr(session, sq_fpr, false, &tpk, NULL);
  2.1273      sq_fingerprint_free(sq_fpr);
  2.1274      ERROR_OUT(session, status, "Looking up TPK for %s", fpr);
  2.1275  
  2.1276 @@ -1628,121 +1627,6 @@
  2.1277      return status;
  2.1278  }
  2.1279  
  2.1280 -static stringpair_list_t *add_key(PEP_SESSION session, stringpair_list_t *k,
  2.1281 -                                  sq_tpk_t tpk, sq_fingerprint_t fpr) {
  2.1282 -    sq_revocation_status_t rs = sq_tpk_revocation_status(tpk);
  2.1283 -    sq_revocation_status_variant_t rsv = sq_revocation_status_variant(rs);
  2.1284 -    sq_revocation_status_free(rs);
  2.1285 -    if (rsv == SQ_REVOCATION_STATUS_REVOKED)
  2.1286 -        return k;
  2.1287 -
  2.1288 -    int dealloc_fpr = 0;
  2.1289 -    if (!fpr) {
  2.1290 -        dealloc_fpr = 1;
  2.1291 -        fpr = sq_tpk_fingerprint(tpk);
  2.1292 -    }
  2.1293 -
  2.1294 -    char *fpr_str = sq_fingerprint_to_hex(fpr);
  2.1295 -    char *user_id = sq_tpk_primary_user_id(tpk);
  2.1296 -    if (user_id) {
  2.1297 -        T("  %s -> %s", fpr_str, user_id);
  2.1298 -        k = stringpair_list_add(k, new_stringpair(fpr_str, user_id));
  2.1299 -    }
  2.1300 -
  2.1301 -    free(user_id);
  2.1302 -    free(fpr_str);
  2.1303 -    if (dealloc_fpr)
  2.1304 -        sq_fingerprint_free(fpr);
  2.1305 -
  2.1306 -    return k;
  2.1307 -}
  2.1308 -
  2.1309 -// pattern could be empty, an fpr, or a mailbox.
  2.1310 -//
  2.1311 -// keyinfo_list is a list of <fpr, openpgp userid> tuples for the
  2.1312 -// matching keys.
  2.1313 -//
  2.1314 -// This function filters out revoked key, but not expired keys.
  2.1315 -PEP_STATUS pgp_list_keyinfo(PEP_SESSION session,
  2.1316 -                            const char* pattern,
  2.1317 -                            stringpair_list_t** keyinfo_list)
  2.1318 -{
  2.1319 -    PEP_STATUS status = PEP_STATUS_OK;
  2.1320 -    sq_tpk_t tpk = NULL;
  2.1321 -    sq_fingerprint_t fpr = NULL;
  2.1322 -
  2.1323 -    T("('%s')", pattern);
  2.1324 -
  2.1325 -    *keyinfo_list = new_stringpair_list(NULL);
  2.1326 -    if (!*keyinfo_list)
  2.1327 -        ERROR_OUT(session, PEP_OUT_OF_MEMORY, "new_stringlist");
  2.1328 -
  2.1329 -    // Trim any leading space.  This also makes it easier to recognize
  2.1330 -    // a string that is only whitespace.
  2.1331 -    while (*pattern == ' ')
  2.1332 -        pattern ++;
  2.1333 -
  2.1334 -    if (strchr(pattern, '@')) {
  2.1335 -        // Looks like a mailbox.
  2.1336 -        status = tpk_find_by_label(session, pattern, &tpk);
  2.1337 -        ERROR_OUT(session, status, "Looking up '%s'", pattern);
  2.1338 -        add_key(session, *keyinfo_list, tpk, NULL);
  2.1339 -
  2.1340 -        assert(!"pgp_list_keyinfo(email) untested, please make a test case");
  2.1341 -    } else if (// Only hex characters and spaces
  2.1342 -               pattern[strspn(pattern, "0123456789aAbBcCdDeEfF ")] == 0
  2.1343 -               // And a fair amount of them.
  2.1344 -               && strlen(pattern) >= 16) {
  2.1345 -        // Fingerprint.
  2.1346 -        fpr = sq_fingerprint_from_hex(pattern);
  2.1347 -        status = tpk_find_by_fpr(session, fpr, &tpk);
  2.1348 -        ERROR_OUT(session, status, "Looking up key");
  2.1349 -        add_key(session, *keyinfo_list, tpk, fpr);
  2.1350 -
  2.1351 -        assert(!"pgp_list_keyinfo(fpr) untested, please make a test case");
  2.1352 -    } else if (pattern[0] == 0) {
  2.1353 -        // Empty string.
  2.1354 -        sq_binding_iter_t iter = sq_store_iter(session->ctx, session->store);
  2.1355 -        sq_binding_t binding;
  2.1356 -        char *label;
  2.1357 -        stringpair_list_t *_k = *keyinfo_list;
  2.1358 -        while ((binding = sq_binding_iter_next(iter, &label, &fpr))) {
  2.1359 -            if (strchr(label, '@')) {
  2.1360 -                char *fpr_str = sq_fingerprint_to_hex(fpr);
  2.1361 -                T("  %s -> %s", fpr_str, label);
  2.1362 -                _k = stringpair_list_add(_k, new_stringpair(fpr_str, label));
  2.1363 -                free(fpr_str);
  2.1364 -            }
  2.1365 -            free(label);
  2.1366 -            sq_fingerprint_free(fpr);
  2.1367 -            fpr = NULL;
  2.1368 -            sq_binding_free(binding);
  2.1369 -        }
  2.1370 -        sq_binding_iter_free(iter);
  2.1371 -    }
  2.1372 -
  2.1373 - out:
  2.1374 -    if (tpk)
  2.1375 -        sq_tpk_free(tpk);
  2.1376 -    if (fpr)
  2.1377 -        sq_fingerprint_free(fpr);
  2.1378 -    if (status != PEP_STATUS_OK && *keyinfo_list) {
  2.1379 -        free_stringpair_list(*keyinfo_list);
  2.1380 -        *keyinfo_list = NULL;
  2.1381 -    }
  2.1382 -    if (status == PEP_KEY_NOT_FOUND)
  2.1383 -        status = PEP_STATUS_OK;
  2.1384 -
  2.1385 -    T("(%s) -> %s", pattern, pep_status_to_string(status));
  2.1386 -    return status;
  2.1387 -}
  2.1388 -
  2.1389 -PEP_STATUS pgp_recv_key(PEP_SESSION session, const char *pattern)
  2.1390 -{
  2.1391 -    assert(!"pgp_recv_key not implemented");
  2.1392 -    return PEP_UNKNOWN_ERROR;
  2.1393 -}
  2.1394 -
  2.1395  char* _undot_address(const char* address) {
  2.1396      if (!address)
  2.1397          return NULL;
  2.1398 @@ -1773,24 +1657,89 @@
  2.1399      return retval;
  2.1400  }
  2.1401  
  2.1402 -// Unlike pgp_list_keyinfo, this function returns revoked keys.
  2.1403 -static PEP_STATUS _pgp_search_keys(PEP_SESSION session, const char* pattern,
  2.1404 -                                   stringlist_t** keylist, int private_only) {
  2.1405 +static stringpair_list_t *add_key(PEP_SESSION session,
  2.1406 +                                  stringpair_list_t *keyinfo_list,
  2.1407 +                                  stringlist_t* keylist,
  2.1408 +                                  sq_tpk_t tpk, sq_fingerprint_t fpr) {
  2.1409 +    bool revoked = false;
  2.1410 +    // Don't add revoked keys to the keyinfo_list.
  2.1411 +    if (keyinfo_list) {
  2.1412 +        sq_revocation_status_t rs = sq_tpk_revocation_status(tpk);
  2.1413 +        sq_revocation_status_variant_t rsv = sq_revocation_status_variant(rs);
  2.1414 +        sq_revocation_status_free(rs);
  2.1415 +        if (rsv == SQ_REVOCATION_STATUS_REVOKED)
  2.1416 +            revoked = true;
  2.1417 +    }
  2.1418 +
  2.1419 +    if (revoked && ! keylist)
  2.1420 +        return keyinfo_list;
  2.1421 +
  2.1422 +    int dealloc_fpr = 0;
  2.1423 +    if (!fpr) {
  2.1424 +        dealloc_fpr = 1;
  2.1425 +        fpr = sq_tpk_fingerprint(tpk);
  2.1426 +    }
  2.1427 +    char *fpr_str = sq_fingerprint_to_hex(fpr);
  2.1428 +
  2.1429 +    if (!revoked && keyinfo_list) {
  2.1430 +        char *user_id = sq_tpk_primary_user_id(tpk);
  2.1431 +        if (user_id)
  2.1432 +            keyinfo_list = stringpair_list_add(keyinfo_list,
  2.1433 +                                               new_stringpair(fpr_str, user_id));
  2.1434 +        free(user_id);
  2.1435 +    }
  2.1436 +
  2.1437 +    if (keylist)
  2.1438 +        keylist = stringlist_add(keylist, fpr_str);
  2.1439 +
  2.1440 +    free(fpr_str);
  2.1441 +    if (dealloc_fpr)
  2.1442 +        sq_fingerprint_free(fpr);
  2.1443 +
  2.1444 +    return keyinfo_list;
  2.1445 +}
  2.1446 +
  2.1447 +static PEP_STATUS list_keys(PEP_SESSION session,
  2.1448 +                            const char* pattern, int private_only,
  2.1449 +                            stringpair_list_t** keyinfo_list, stringlist_t** keylist)
  2.1450 +{
  2.1451      PEP_STATUS status = PEP_STATUS_OK;
  2.1452 -    sq_binding_t binding = NULL;
  2.1453      sq_tpk_t tpk = NULL;
  2.1454 -    sq_fingerprint_t fingerprint = NULL;
  2.1455 -    char *fingerprint_hex = NULL;
  2.1456 +    sq_fingerprint_t fpr = NULL;
  2.1457  
  2.1458 -    *keylist = NULL;
  2.1459 +    T("('%s', private: %d)", pattern, private_only);
  2.1460  
  2.1461 -    // XXX: We only return an exact match.
  2.1462 -    T("(pattern: %s, private_only: %d)", pattern, private_only);
  2.1463 +    stringpair_list_t* _keyinfo_list = NULL;
  2.1464 +    if (keyinfo_list) {
  2.1465 +        _keyinfo_list = new_stringpair_list(NULL);
  2.1466 +        if (!_keyinfo_list)
  2.1467 +            ERROR_OUT(session, PEP_OUT_OF_MEMORY, "new_stringpair_list");
  2.1468 +    }
  2.1469 +    stringlist_t* _keylist = NULL;
  2.1470 +    if (keylist) {
  2.1471 +        _keylist = new_stringlist(NULL);
  2.1472 +        if (!_keylist)
  2.1473 +            ERROR_OUT(session, PEP_OUT_OF_MEMORY, "new_string_list");
  2.1474 +    }
  2.1475  
  2.1476 -    binding = sq_store_lookup(session->ctx, session->store, pattern);
  2.1477 -    if (! binding) {
  2.1478 -        // No binding is not an error: it means there is no match.
  2.1479 -        if (pattern != NULL) {
  2.1480 +    // Trim any leading space.  This also makes it easier to recognize
  2.1481 +    // a string that is only whitespace.
  2.1482 +    while (*pattern == ' ')
  2.1483 +        pattern ++;
  2.1484 +
  2.1485 +    if (strchr(pattern, '@')) {
  2.1486 +        // Looks like a mailbox.
  2.1487 +        sq_tpk_t *tpks = NULL;
  2.1488 +        int count = 0;
  2.1489 +        status = tpk_find_by_email(session, pattern, private_only, &tpks, &count);
  2.1490 +        ERROR_OUT(session, status, "Looking up '%s'", pattern);
  2.1491 +        for (int i = 0; i < count; i ++) {
  2.1492 +            add_key(session, _keyinfo_list, _keylist, tpks[i], NULL);
  2.1493 +            sq_tpk_free(tpks[i]);
  2.1494 +        }
  2.1495 +        free(tpks);
  2.1496 +
  2.1497 +        if (count == 0) {
  2.1498              // If match failed, check to see if we've got a dotted
  2.1499              // address in the pattern.  If so, try again without dots.
  2.1500              const char* dotpos = strstr(pattern, ".");
  2.1501 @@ -1798,63 +1747,101 @@
  2.1502              if (dotpos && atpos && (dotpos < atpos)) {
  2.1503                  char* undotted = _undot_address(pattern);
  2.1504                  if (undotted) {
  2.1505 -                    PEP_STATUS status = _pgp_search_keys(session, undotted,
  2.1506 -                                                         keylist, private_only);
  2.1507 +                    PEP_STATUS status = list_keys(session, undotted, private_only,
  2.1508 +                                                  keyinfo_list, keylist);
  2.1509                      free(undotted);
  2.1510                      return status;
  2.1511                  }
  2.1512              }
  2.1513          }
  2.1514 -        goto out;
  2.1515 +    } else if (// Only hex characters and spaces
  2.1516 +               pattern[strspn(pattern, "0123456789aAbBcCdDeEfF ")] == 0
  2.1517 +               // And a fair amount of them.
  2.1518 +               && strlen(pattern) >= 16) {
  2.1519 +        // Fingerprint.
  2.1520 +        fpr = sq_fingerprint_from_hex(pattern);
  2.1521 +        status = tpk_find_by_fpr(session, fpr, false, &tpk, NULL);
  2.1522 +        ERROR_OUT(session, status, "Looking up key");
  2.1523 +        add_key(session, _keyinfo_list, _keylist, tpk, fpr);
  2.1524 +    } else if (pattern[0] == 0) {
  2.1525 +        // Empty string.
  2.1526 +
  2.1527 +        sq_tpk_t *tpks = NULL;
  2.1528 +        int count = 0;
  2.1529 +        status = tpk_all(session, private_only, &tpks, &count);
  2.1530 +        ERROR_OUT(session, status, "Looking up '%s'", pattern);
  2.1531 +        for (int i = 0; i < count; i ++) {
  2.1532 +            add_key(session, _keyinfo_list, _keylist, tpks[i], NULL);
  2.1533 +            sq_tpk_free(tpks[i]);
  2.1534 +        }
  2.1535 +        free(tpks);
  2.1536 +    } else {
  2.1537 +        T("unsupported pattern '%s'", pattern);
  2.1538      }
  2.1539  
  2.1540 -    tpk = sq_binding_tpk(session->ctx, binding);
  2.1541 -    if (! tpk)
  2.1542 -        ERROR_OUT(session, PEP_GET_KEY_FAILED, "Getting TPK");
  2.1543 -
  2.1544 -    fingerprint = sq_tpk_fingerprint(tpk);
  2.1545 -    if (!fingerprint)
  2.1546 -        ERROR_OUT(session, PEP_OUT_OF_MEMORY, "Getting fingerprint");
  2.1547 -
  2.1548 -    if (private_only) {
  2.1549 -        // See if we have the private key.
  2.1550 -        status = tsk_find_by_fpr(session, fingerprint, NULL);
  2.1551 -        ERROR_OUT(session, status, "No private key material");
  2.1552 -    }
  2.1553 -
  2.1554 -    fingerprint_hex = sq_fingerprint_to_hex(fingerprint);
  2.1555 -    if (!fingerprint_hex)
  2.1556 -        ERROR_OUT(session, PEP_OUT_OF_MEMORY, "sq_fingerprint_to_hex");
  2.1557 -
  2.1558 -    stringlist_t *_keylist = new_stringlist(fingerprint_hex);
  2.1559 -    if (_keylist == NULL)
  2.1560 -        ERROR_OUT(session, PEP_OUT_OF_MEMORY, "new_stringlist");
  2.1561 -    *keylist = _keylist;
  2.1562 -
  2.1563   out:
  2.1564 -    free(fingerprint_hex);
  2.1565 -    if (fingerprint)
  2.1566 -        sq_fingerprint_free(fingerprint);
  2.1567      if (tpk)
  2.1568          sq_tpk_free(tpk);
  2.1569 -    if (binding)
  2.1570 -        sq_binding_free(binding);
  2.1571 +    if (fpr)
  2.1572 +        sq_fingerprint_free(fpr);
  2.1573  
  2.1574 -    T("(pattern: %s, private_only: %d) -> %s",
  2.1575 -      pattern, private_only, pep_status_to_string(status));
  2.1576 +    if (status == PEP_KEY_NOT_FOUND)
  2.1577 +        status = PEP_STATUS_OK;
  2.1578 +
  2.1579 +    if (status != PEP_STATUS_OK || (_keyinfo_list && !_keyinfo_list->value)) {
  2.1580 +        free_stringpair_list(_keyinfo_list);
  2.1581 +        _keyinfo_list = NULL;
  2.1582 +    }
  2.1583 +    if (keyinfo_list)
  2.1584 +        *keyinfo_list = _keyinfo_list;
  2.1585 +
  2.1586 +    if (status != PEP_STATUS_OK || (_keylist && !_keylist->value)) {
  2.1587 +        free_stringlist(_keylist);
  2.1588 +        _keylist = NULL;
  2.1589 +    }
  2.1590 +    if (keylist)
  2.1591 +        *keylist = _keylist;
  2.1592 +
  2.1593 +    int len = -1;
  2.1594 +    if (keylist)
  2.1595 +        len = stringlist_length(*keylist);
  2.1596 +    else if (keyinfo_list)
  2.1597 +        len = stringpair_list_length(*keyinfo_list);
  2.1598 +    T("(%s) -> %s (%d keys)", pattern, pep_status_to_string(status), len);
  2.1599      return status;
  2.1600  }
  2.1601  
  2.1602 +// pattern could be empty, an fpr, or a mailbox.
  2.1603 +//
  2.1604 +// keyinfo_list is a list of <fpr, openpgp userid> tuples for the
  2.1605 +// matching keys.
  2.1606 +//
  2.1607 +// This function filters out revoked key, but not expired keys.
  2.1608 +PEP_STATUS pgp_list_keyinfo(PEP_SESSION session,
  2.1609 +                            const char* pattern,
  2.1610 +                            stringpair_list_t** keyinfo_list)
  2.1611 +{
  2.1612 +    return list_keys(session, pattern, false, keyinfo_list, NULL);
  2.1613 +}
  2.1614 +
  2.1615 +PEP_STATUS pgp_recv_key(PEP_SESSION session, const char *pattern)
  2.1616 +{
  2.1617 +    assert(!"pgp_recv_key not implemented");
  2.1618 +    return PEP_UNKNOWN_ERROR;
  2.1619 +}
  2.1620 +
  2.1621 +// Unlike pgp_list_keyinfo, this function returns revoked keys.
  2.1622  PEP_STATUS pgp_find_keys(
  2.1623      PEP_SESSION session, const char *pattern, stringlist_t **keylist)
  2.1624  {
  2.1625 -    return _pgp_search_keys(session, pattern, keylist, 0);
  2.1626 +    return list_keys(session, pattern, false, NULL, keylist);
  2.1627  }
  2.1628  
  2.1629 +// Unlike pgp_list_keyinfo, this function returns revoked keys.
  2.1630  PEP_STATUS pgp_find_private_keys(
  2.1631      PEP_SESSION session, const char *pattern, stringlist_t **keylist)
  2.1632  {
  2.1633 -    return _pgp_search_keys(session, pattern, keylist, 1);
  2.1634 +    return list_keys(session, pattern, true, NULL, keylist);
  2.1635  }
  2.1636  
  2.1637  PEP_STATUS pgp_send_key(PEP_SESSION session, const char *pattern)
  2.1638 @@ -1876,7 +1863,7 @@
  2.1639      *comm_type = PEP_ct_unknown;
  2.1640  
  2.1641      sq_fingerprint_t sq_fpr = sq_fingerprint_from_hex(fpr);
  2.1642 -    status = tpk_find_by_fpr(session, sq_fpr, &tpk);
  2.1643 +    status = tpk_find_by_fpr(session, sq_fpr, false, &tpk, NULL);
  2.1644      sq_fingerprint_free(sq_fpr);
  2.1645      ERROR_OUT(session, status, "Looking up key: %s", fpr);
  2.1646  
  2.1647 @@ -1959,16 +1946,14 @@
  2.1648      PEP_SESSION session, const char *fpr, const timestamp *ts)
  2.1649  {
  2.1650      PEP_STATUS status = PEP_STATUS_OK;
  2.1651 -    sq_tsk_t tsk = NULL;
  2.1652      sq_tpk_t tpk = NULL;
  2.1653 -
  2.1654      time_t t = mktime((struct tm *) ts);
  2.1655  
  2.1656 -    status = tsk_find_by_fpr_hex(session, fpr, &tsk);
  2.1657 +    T("(%s)", fpr);
  2.1658 +
  2.1659 +    status = tpk_find_by_fpr_hex(session, fpr, true, &tpk, NULL);
  2.1660      ERROR_OUT(session, status, "Looking up '%s'", fpr);
  2.1661  
  2.1662 -    tpk = sq_tsk_into_tpk(tsk);
  2.1663 -
  2.1664      uint32_t creation_time = sq_p_key_creation_time(sq_tpk_primary(tpk));
  2.1665      if (creation_time > t)
  2.1666          // The creation time is after the expiration time!
  2.1667 @@ -1980,7 +1965,7 @@
  2.1668      if (! tpk)
  2.1669          ERROR_OUT(session, PEP_UNKNOWN_ERROR, "setting expiration");
  2.1670  
  2.1671 -    status = tpk_save(session, tpk, NULL, NULL, false);
  2.1672 +    status = tpk_save(session, tpk, NULL);
  2.1673      tpk = NULL;
  2.1674      ERROR_OUT(session, status, "Saving %s", fpr);
  2.1675  
  2.1676 @@ -1988,6 +1973,7 @@
  2.1677      if (tpk)
  2.1678          sq_tpk_free(tpk);
  2.1679  
  2.1680 +    T("(%s) -> %s", fpr, pep_status_to_string(status));
  2.1681      return status;
  2.1682  }
  2.1683  
  2.1684 @@ -1995,13 +1981,13 @@
  2.1685      PEP_SESSION session, const char *fpr, const char *reason)
  2.1686  {
  2.1687      PEP_STATUS status = PEP_STATUS_OK;
  2.1688 -    sq_tsk_t tsk = NULL;
  2.1689      sq_tpk_t tpk = NULL;
  2.1690  
  2.1691 -    status = tsk_find_by_fpr_hex(session, fpr, &tsk);
  2.1692 +    T("(%s)", fpr);
  2.1693 +
  2.1694 +    status = tpk_find_by_fpr_hex(session, fpr, true, &tpk, NULL);
  2.1695      ERROR_OUT(session, status, "Looking up %s", fpr);
  2.1696  
  2.1697 -    tpk = sq_tsk_into_tpk(tsk);
  2.1698      tpk = sq_tpk_revoke_in_place(session->ctx, tpk,
  2.1699                                   SQ_REASON_FOR_REVOCATION_UNSPECIFIED,
  2.1700                                   reason);
  2.1701 @@ -2011,7 +1997,7 @@
  2.1702      assert(sq_revocation_status_variant(sq_tpk_revocation_status(tpk))
  2.1703             == SQ_REVOCATION_STATUS_REVOKED);
  2.1704  
  2.1705 -    status = tpk_save(session, tpk, NULL, NULL, false);
  2.1706 +    status = tpk_save(session, tpk, NULL);
  2.1707      tpk = NULL;
  2.1708      ERROR_OUT(session, status, "Saving %s", fpr);
  2.1709  
  2.1710 @@ -2019,6 +2005,7 @@
  2.1711      if (tpk)
  2.1712          sq_tpk_free(tpk);
  2.1713  
  2.1714 +    T("(%s) -> %s", fpr, pep_status_to_string(status));
  2.1715      return status;
  2.1716  }
  2.1717  
  2.1718 @@ -2027,6 +2014,7 @@
  2.1719  {
  2.1720      PEP_STATUS status = PEP_STATUS_OK;
  2.1721      sq_tpk_t tpk = NULL;
  2.1722 +    T("(%s)", fpr);
  2.1723  
  2.1724      assert(session);
  2.1725      assert(fpr);
  2.1726 @@ -2035,7 +2023,7 @@
  2.1727      *expired = false;
  2.1728  
  2.1729      sq_fingerprint_t sq_fpr = sq_fingerprint_from_hex(fpr);
  2.1730 -    status = tpk_find_by_fpr(session, sq_fpr, &tpk);
  2.1731 +    status = tpk_find_by_fpr(session, sq_fpr, false, &tpk, NULL);
  2.1732      sq_fingerprint_free(sq_fpr);
  2.1733      ERROR_OUT(session, status, "Looking up %s", fpr);
  2.1734  
  2.1735 @@ -2080,6 +2068,7 @@
  2.1736   out:
  2.1737      if (tpk)
  2.1738          sq_tpk_free(tpk);
  2.1739 +    T("(%s) -> %s", fpr, pep_status_to_string(status));
  2.1740      return status;
  2.1741  }
  2.1742  
  2.1743 @@ -2088,6 +2077,8 @@
  2.1744      PEP_STATUS status = PEP_STATUS_OK;
  2.1745      sq_tpk_t tpk;
  2.1746  
  2.1747 +    T("(%s)", fpr);
  2.1748 +
  2.1749      assert(session);
  2.1750      assert(fpr);
  2.1751      assert(revoked);
  2.1752 @@ -2095,7 +2086,7 @@
  2.1753      *revoked = false;
  2.1754  
  2.1755      sq_fingerprint_t sq_fpr = sq_fingerprint_from_hex(fpr);
  2.1756 -    status = tpk_find_by_fpr(session, sq_fpr, &tpk);
  2.1757 +    status = tpk_find_by_fpr(session, sq_fpr, false, &tpk, NULL);
  2.1758      sq_fingerprint_free(sq_fpr);
  2.1759      ERROR_OUT(session, status, "Looking up %s", fpr);
  2.1760  
  2.1761 @@ -2105,6 +2096,7 @@
  2.1762      sq_tpk_free(tpk);
  2.1763  
  2.1764   out:
  2.1765 +    T("(%s) -> %s", fpr, pep_status_to_string(status));
  2.1766      return status;
  2.1767  }
  2.1768  
  2.1769 @@ -2112,11 +2104,12 @@
  2.1770  {
  2.1771      PEP_STATUS status = PEP_STATUS_OK;
  2.1772      sq_tpk_t tpk = NULL;
  2.1773 +    T("(%s)", fpr);
  2.1774  
  2.1775      *created = 0;
  2.1776  
  2.1777      sq_fingerprint_t sq_fpr = sq_fingerprint_from_hex(fpr);
  2.1778 -    status = tpk_find_by_fpr(session, sq_fpr, &tpk);
  2.1779 +    status = tpk_find_by_fpr(session, sq_fpr, false, &tpk, NULL);
  2.1780      sq_fingerprint_free(sq_fpr);
  2.1781      ERROR_OUT(session, status, "Looking up %s", fpr);
  2.1782  
  2.1783 @@ -2125,6 +2118,7 @@
  2.1784      sq_tpk_free(tpk);
  2.1785  
  2.1786   out:
  2.1787 +    T("(%s) -> %s", fpr, pep_status_to_string(status));
  2.1788      return status;
  2.1789  }
  2.1790  
  2.1791 @@ -2136,16 +2130,16 @@
  2.1792  PEP_STATUS pgp_contains_priv_key(PEP_SESSION session, const char *fpr,
  2.1793                                   bool *has_private)
  2.1794  {
  2.1795 +    T("(%s)", fpr);
  2.1796      sq_fingerprint_t sq_fpr = sq_fingerprint_from_hex(fpr);
  2.1797 -    PEP_STATUS status = tsk_find_by_fpr(session, sq_fpr, NULL);
  2.1798 +    PEP_STATUS status = tpk_find_by_fpr(session, sq_fpr, true, NULL, NULL);
  2.1799      sq_fingerprint_free(sq_fpr);
  2.1800      if (status == PEP_STATUS_OK) {
  2.1801          *has_private = 1;
  2.1802 -        return PEP_STATUS_OK;
  2.1803      } else if (status == PEP_KEY_NOT_FOUND) {
  2.1804          *has_private = 0;
  2.1805 -        return PEP_STATUS_OK;
  2.1806 -    } else {
  2.1807 -        return status;
  2.1808 +        status = PEP_STATUS_OK;
  2.1809      }
  2.1810 +    T("(%s) -> %s", fpr, pep_status_to_string(status));
  2.1811 +    return status;
  2.1812  }