src/pgp_sequoia.c
branchsync
changeset 3209 c15b4ca2b52a
parent 3197 6eb348201d7f
child 3211 8ca54e726b3c
     1.1 --- a/src/pgp_sequoia.c	Thu Dec 20 15:28:21 2018 +0100
     1.2 +++ b/src/pgp_sequoia.c	Tue Dec 25 14:46:45 2018 +0100
     1.3 @@ -72,11 +72,6 @@
     1.4          ERROR_OUT(session, PEP_INIT_GPGME_INIT_FAILED,
     1.5                    "initializing sequoia context");
     1.6  
     1.7 -    session->store = sq_store_open(session->ctx, "foundation.pep");
     1.8 -    if (session->store == NULL)
     1.9 -        ERROR_OUT(session, PEP_INIT_GPGME_INIT_FAILED, "opening the store");
    1.10 -
    1.11 -
    1.12      // Create the home directory.
    1.13      char *home_env = getenv("HOME");
    1.14      if (!home_env)
    1.15 @@ -114,9 +109,12 @@
    1.16  
    1.17      sqlite_result = sqlite3_exec(session->key_db,
    1.18                                   "CREATE TABLE IF NOT EXISTS keys (\n"
    1.19 -                                 "   primary_key TEXT PRIMARY KEY,\n"
    1.20 -                                 "   tsk BLOB\n"
    1.21 -                                 ");\n",
    1.22 +                                 "   primary_key TEXT UNIQUE PRIMARY KEY,\n"
    1.23 +                                 "   secret BOOLEAN,\n"
    1.24 +                                 "   tpk BLOB\n"
    1.25 +                                 ");\n"
    1.26 +                                 "CREATE INDEX IF NOT EXISTS keys_index\n"
    1.27 +                                 "  ON keys (primary_key, secret)\n",
    1.28                                   NULL, NULL, NULL);
    1.29      if (sqlite_result != SQLITE_OK)
    1.30          ERROR_OUT(session, PEP_INIT_CANNOT_OPEN_DB,
    1.31 @@ -125,18 +123,38 @@
    1.32  
    1.33      sqlite_result = sqlite3_exec(session->key_db,
    1.34                                   "CREATE TABLE IF NOT EXISTS subkeys (\n"
    1.35 -                                 "   subkey TEXT PRIMARY KEY,\n"
    1.36 -                                 "   primary_key TEXT,\n"
    1.37 +                                 "   subkey TEXT NOT NULL,\n"
    1.38 +                                 "   primary_key TEXT NOT NULL,\n"
    1.39 +                                 "   UNIQUE(subkey, primary_key),\n"
    1.40                                   "   FOREIGN KEY (primary_key)\n"
    1.41                                   "       REFERENCES keys(primary_key)\n"
    1.42                                   "     ON DELETE CASCADE\n"
    1.43 -                                 ");\n",
    1.44 +                                 ");\n"
    1.45 +                                 "CREATE INDEX IF NOT EXISTS subkeys_index\n"
    1.46 +                                 "  ON subkeys (subkey, primary_key)\n",
    1.47                                   NULL, NULL, NULL);
    1.48      if (sqlite_result != SQLITE_OK)
    1.49          ERROR_OUT(session, PEP_INIT_CANNOT_OPEN_DB,
    1.50                    "creating subkeys table: %s",
    1.51                    sqlite3_errmsg(session->key_db));
    1.52  
    1.53 +    sqlite_result = sqlite3_exec(session->key_db,
    1.54 +                                 "CREATE TABLE IF NOT EXISTS userids (\n"
    1.55 +                                 "   userid TEXT NOT NULL,\n"
    1.56 +                                 "   primary_key TEXT NOT NULL,\n"
    1.57 +                                 "   UNIQUE(userid, primary_key),\n"
    1.58 +                                 "   FOREIGN KEY (primary_key)\n"
    1.59 +                                 "       REFERENCES keys(primary_key)\n"
    1.60 +                                 "     ON DELETE CASCADE\n"
    1.61 +                                 ");\n"
    1.62 +                                 "CREATE INDEX IF NOT EXISTS userids_index\n"
    1.63 +                                 "  ON userids (userid, primary_key)\n",
    1.64 +                                 NULL, NULL, NULL);
    1.65 +    if (sqlite_result != SQLITE_OK)
    1.66 +        ERROR_OUT(session, PEP_INIT_CANNOT_OPEN_DB,
    1.67 +                  "creating userids table: %s",
    1.68 +                  sqlite3_errmsg(session->key_db));
    1.69 +
    1.70      sqlite_result
    1.71          = sqlite3_prepare_v2(session->key_db, "begin transaction",
    1.72                               -1, &session->begin_transaction, NULL);
    1.73 @@ -154,10 +172,81 @@
    1.74  
    1.75      sqlite_result
    1.76          = sqlite3_prepare_v2(session->key_db,
    1.77 +                             "SELECT tpk, secret FROM keys"
    1.78 +                             " WHERE primary_key == ?",
    1.79 +                             -1, &session->tpk_find, NULL);
    1.80 +    assert(sqlite_result == SQLITE_OK);
    1.81 +
    1.82 +    sqlite_result
    1.83 +        = sqlite3_prepare_v2(session->key_db,
    1.84 +                             "SELECT tpk, secret FROM keys"
    1.85 +                             " WHERE primary_key == ? and secret == 1",
    1.86 +                             -1, &session->tsk_find, NULL);
    1.87 +    assert(sqlite_result == SQLITE_OK);
    1.88 +
    1.89 +    sqlite_result
    1.90 +        = sqlite3_prepare_v2(session->key_db,
    1.91 +                             "SELECT tpk, secret FROM subkeys"
    1.92 +                             " LEFT JOIN keys"
    1.93 +                             "  ON subkeys.primary_key == keys.primary_key"
    1.94 +                             " WHERE subkey == ?",
    1.95 +                             -1, &session->tpk_find_by_keyid, NULL);
    1.96 +    assert(sqlite_result == SQLITE_OK);
    1.97 +
    1.98 +    sqlite_result
    1.99 +        = sqlite3_prepare_v2(session->key_db,
   1.100 +                             "SELECT tpk, secret FROM subkeys"
   1.101 +                             " LEFT JOIN keys"
   1.102 +                             "  ON subkeys.primary_key == keys.primary_key"
   1.103 +                             " WHERE subkey == ?",
   1.104 +                             -1, &session->tpk_find_by_keyid, NULL);
   1.105 +    assert(sqlite_result == SQLITE_OK);
   1.106 +
   1.107 +    sqlite_result
   1.108 +        = sqlite3_prepare_v2(session->key_db,
   1.109 +                             "SELECT tpk, secret FROM subkeys"
   1.110 +                             " LEFT JOIN keys"
   1.111 +                             "  ON subkeys.primary_key == keys.primary_key"
   1.112 +                             " WHERE subkey == ? and keys.secret == 1",
   1.113 +                             -1, &session->tsk_find_by_keyid, NULL);
   1.114 +    assert(sqlite_result == SQLITE_OK);
   1.115 +
   1.116 +    sqlite_result
   1.117 +        = sqlite3_prepare_v2(session->key_db,
   1.118 +                             "SELECT tpk, secret FROM userids"
   1.119 +                             " LEFT JOIN keys"
   1.120 +                             "  ON userids.primary_key == keys.primary_key"
   1.121 +                             " WHERE userid == ?",
   1.122 +                             -1, &session->tpk_find_by_email, NULL);
   1.123 +    assert(sqlite_result == SQLITE_OK);
   1.124 +
   1.125 +    sqlite_result
   1.126 +        = sqlite3_prepare_v2(session->key_db,
   1.127 +                             "SELECT tpk, secret FROM userids"
   1.128 +                             " LEFT JOIN keys"
   1.129 +                             "  ON userids.primary_key == keys.primary_key"
   1.130 +                             " WHERE userid == ? and keys.secret == 1",
   1.131 +                             -1, &session->tsk_find_by_email, NULL);
   1.132 +    assert(sqlite_result == SQLITE_OK);
   1.133 +
   1.134 +    sqlite_result
   1.135 +        = sqlite3_prepare_v2(session->key_db,
   1.136 +                             "select tpk, secret from keys",
   1.137 +                             -1, &session->tpk_all, NULL);
   1.138 +    assert(sqlite_result == SQLITE_OK);
   1.139 +
   1.140 +    sqlite_result
   1.141 +        = sqlite3_prepare_v2(session->key_db,
   1.142 +                             "select tpk, secret from keys where secret = 1",
   1.143 +                             -1, &session->tsk_all, NULL);
   1.144 +    assert(sqlite_result == SQLITE_OK);
   1.145 +
   1.146 +    sqlite_result
   1.147 +        = sqlite3_prepare_v2(session->key_db,
   1.148                               "INSERT OR REPLACE INTO keys"
   1.149 -                             "   (primary_key, tsk)"
   1.150 -                             " VALUES (?, ?)",
   1.151 -                             -1, &session->tsk_save_insert_primary, NULL);
   1.152 +                             "   (primary_key, secret, tpk)"
   1.153 +                             " VALUES (?, ?, ?)",
   1.154 +                             -1, &session->tpk_save_insert_primary, NULL);
   1.155      assert(sqlite_result == SQLITE_OK);
   1.156  
   1.157      sqlite_result
   1.158 @@ -165,22 +254,15 @@
   1.159                               "INSERT OR REPLACE INTO subkeys"
   1.160                               "   (subkey, primary_key)"
   1.161                               " VALUES (?, ?)",
   1.162 -                             -1, &session->tsk_save_insert_subkeys, NULL);
   1.163 -    assert(sqlite_result == SQLITE_OK);
   1.164 -
   1.165 -    sqlite_result
   1.166 -        = sqlite3_prepare_v2(session->key_db, "select tsk from keys",
   1.167 -                             -1, &session->tsk_all, NULL);
   1.168 +                             -1, &session->tpk_save_insert_subkeys, NULL);
   1.169      assert(sqlite_result == SQLITE_OK);
   1.170  
   1.171      sqlite_result
   1.172          = sqlite3_prepare_v2(session->key_db,
   1.173 -                             "SELECT keys.tsk FROM subkeys"
   1.174 -                             " LEFT JOIN keys"
   1.175 -                             "  ON subkeys.primary_key"
   1.176 -                             "     == keys.primary_key"
   1.177 -                             " WHERE subkey == ?",
   1.178 -                             -1, &session->tsk_find_by_keyid, NULL);
   1.179 +                             "INSERT OR REPLACE INTO userids"
   1.180 +                             "   (userid, primary_key)"
   1.181 +                             " VALUES (?, ?)",
   1.182 +                             -1, &session->tpk_save_insert_userids, NULL);
   1.183      assert(sqlite_result == SQLITE_OK);
   1.184  
   1.185   out:
   1.186 @@ -200,18 +282,44 @@
   1.187      if (session->rollback_transaction)
   1.188          sqlite3_finalize(session->rollback_transaction);
   1.189      session->rollback_transaction = NULL;
   1.190 -    if (session->tsk_save_insert_primary)
   1.191 -        sqlite3_finalize(session->tsk_save_insert_primary);
   1.192 -    session->tsk_save_insert_primary = NULL;
   1.193 -    if (session->tsk_save_insert_subkeys)
   1.194 -        sqlite3_finalize(session->tsk_save_insert_subkeys);
   1.195 -    session->tsk_save_insert_subkeys = NULL;
   1.196 +
   1.197 +    if (session->tpk_find)
   1.198 +        sqlite3_finalize(session->tpk_find);
   1.199 +    session->tpk_find = NULL;
   1.200 +    if (session->tsk_find)
   1.201 +        sqlite3_finalize(session->tsk_find);
   1.202 +    session->tsk_find = NULL;
   1.203 +
   1.204 +    if (session->tpk_find_by_keyid)
   1.205 +        sqlite3_finalize(session->tpk_find_by_keyid);
   1.206 +    session->tpk_find_by_keyid = NULL;
   1.207 +    if (session->tsk_find_by_keyid)
   1.208 +        sqlite3_finalize(session->tsk_find_by_keyid);
   1.209 +    session->tsk_find_by_keyid = NULL;
   1.210 +
   1.211 +    if (session->tpk_find_by_email)
   1.212 +        sqlite3_finalize(session->tpk_find_by_email);
   1.213 +    session->tpk_find_by_email = NULL;
   1.214 +    if (session->tsk_find_by_email)
   1.215 +        sqlite3_finalize(session->tsk_find_by_email);
   1.216 +    session->tsk_find_by_email = NULL;
   1.217 +
   1.218 +    if (session->tpk_all)
   1.219 +        sqlite3_finalize(session->tpk_all);
   1.220 +    session->tpk_all = NULL;
   1.221      if (session->tsk_all)
   1.222          sqlite3_finalize(session->tsk_all);
   1.223      session->tsk_all = NULL;
   1.224 -    if (session->tsk_find_by_keyid)
   1.225 -        sqlite3_finalize(session->tsk_find_by_keyid);
   1.226 -    session->tsk_find_by_keyid = NULL;
   1.227 +
   1.228 +    if (session->tpk_save_insert_primary)
   1.229 +        sqlite3_finalize(session->tpk_save_insert_primary);
   1.230 +    session->tpk_save_insert_primary = NULL;
   1.231 +    if (session->tpk_save_insert_subkeys)
   1.232 +        sqlite3_finalize(session->tpk_save_insert_subkeys);
   1.233 +    session->tpk_save_insert_subkeys = NULL;
   1.234 +    if (session->tpk_save_insert_userids)
   1.235 +        sqlite3_finalize(session->tpk_save_insert_userids);
   1.236 +    session->tpk_save_insert_userids = NULL;
   1.237  
   1.238      if (session->key_db) {
   1.239          int result = sqlite3_close_v2(session->key_db);
   1.240 @@ -222,11 +330,6 @@
   1.241          session->key_db = NULL;
   1.242      }
   1.243  
   1.244 -    if (session->store) {
   1.245 -        sq_store_free(session->store);
   1.246 -        session->store = NULL;
   1.247 -    }
   1.248 -
   1.249      if (session->ctx) {
   1.250          sq_context_free(session->ctx);
   1.251          session->ctx = NULL;
   1.252 @@ -300,37 +403,28 @@
   1.253      free(user_id);
   1.254  }
   1.255  
   1.256 -
   1.257 -// Returns the TSK identified by the provided keyid.
   1.258 -//
   1.259 -// If tsk is NULL, the TSK is not parsed and this function simply
   1.260 -// returns whether the key is locally available.
   1.261 -static PEP_STATUS tsk_find_by_keyid_hex(PEP_SESSION, const char *, sq_tsk_t *)
   1.262 -  __attribute__((nonnull(1, 2)));
   1.263 -static PEP_STATUS tsk_find_by_keyid_hex(
   1.264 -        PEP_SESSION session,
   1.265 -        const char *keyid_hex,
   1.266 -        sq_tsk_t *tsk)
   1.267 +// step statement and load the tpk and secret.
   1.268 +static PEP_STATUS key_load(PEP_SESSION, sqlite3_stmt *, sq_tpk_t *, int *)
   1.269 +    __attribute__((nonnull(1, 2)));
   1.270 +static PEP_STATUS key_load(PEP_SESSION session, sqlite3_stmt *stmt,
   1.271 +                           sq_tpk_t *tpkp, int *secretp)
   1.272  {
   1.273      PEP_STATUS status = PEP_STATUS_OK;
   1.274 -    T("%s", keyid_hex);
   1.275 -
   1.276 -    sqlite3_stmt *stmt = session->tsk_find_by_keyid;
   1.277 -    sqlite3_bind_text(stmt, 1, keyid_hex, -1, SQLITE_STATIC);
   1.278      int sqlite_result = sqlite3_step(stmt);
   1.279      switch (sqlite_result) {
   1.280      case SQLITE_ROW:
   1.281 -        if (tsk) {
   1.282 -            // Get the TSK from the first column.
   1.283 +        if (tpkp) {
   1.284              int data_len = sqlite3_column_bytes(stmt, 0);
   1.285              const void *data = sqlite3_column_blob(stmt, 0);
   1.286  
   1.287 -            sq_tpk_t tpk = sq_tpk_from_bytes(session->ctx, data, data_len);
   1.288 -            if (!tpk)
   1.289 +            *tpkp = sq_tpk_from_bytes(session->ctx, data, data_len);
   1.290 +            if (!*tpkp)
   1.291                  ERROR_OUT(session, PEP_GET_KEY_FAILED, "parsing TPK");
   1.292 +        }
   1.293  
   1.294 -            *tsk = sq_tpk_into_tsk(tpk);
   1.295 -        }
   1.296 +        if (secretp)
   1.297 +            *secretp = sqlite3_column_int(stmt, 1);
   1.298 +
   1.299          break;
   1.300      case SQLITE_DONE:
   1.301          // Got nothing.
   1.302 @@ -338,64 +432,214 @@
   1.303          break;
   1.304      default:
   1.305          ERROR_OUT(session, PEP_UNKNOWN_ERROR,
   1.306 -                  "stepping tsk_find_by_keyid: %s",
   1.307 -                  sqlite3_errmsg(session->key_db));
   1.308 +                  "stepping: %s", sqlite3_errmsg(session->key_db));
   1.309      }
   1.310  
   1.311   out:
   1.312 -    sqlite3_reset(stmt);
   1.313 -    T("%s -> %s", keyid_hex, pep_status_to_string(status));
   1.314 +    T(" -> %s", pep_status_to_string(status));
   1.315      return status;
   1.316  }
   1.317  
   1.318 -// See tsk_find_by_keyid_hex.
   1.319 -PEP_STATUS tsk_find_by_keyid(PEP_SESSION, sq_keyid_t, sq_tsk_t *)
   1.320 +// step statement until exhausted and load the tpks.
   1.321 +static PEP_STATUS key_loadn(PEP_SESSION, sqlite3_stmt *, sq_tpk_t **, int *)
   1.322 +    __attribute__((nonnull));
   1.323 +static PEP_STATUS key_loadn(PEP_SESSION session, sqlite3_stmt *stmt,
   1.324 +                            sq_tpk_t **tpksp, int *tpks_countp)
   1.325 +{
   1.326 +    PEP_STATUS status = PEP_STATUS_OK;
   1.327 +    int tpks_count = 0;
   1.328 +    int tpks_capacity = 8;
   1.329 +    sq_tpk_t *tpks = calloc(tpks_capacity, sizeof(sq_tpk_t));
   1.330 +    if (!tpks)
   1.331 +        ERROR_OUT(session, PEP_OUT_OF_MEMORY, "out of memory");
   1.332 +
   1.333 +    for (;;) {
   1.334 +        sq_tpk_t tpk = NULL;
   1.335 +        status = key_load(session, stmt, &tpk, NULL);
   1.336 +        if (status == PEP_KEY_NOT_FOUND) {
   1.337 +            status = PEP_STATUS_OK;
   1.338 +            break;
   1.339 +        }
   1.340 +        ERROR_OUT(session, status, "loading TPK");
   1.341 +
   1.342 +        if (tpks_count == tpks_capacity) {
   1.343 +            tpks_capacity *= 2;
   1.344 +            tpks = realloc(tpks, sizeof(tpks[0]) * tpks_capacity);
   1.345 +            if (!tpks)
   1.346 +                ERROR_OUT(session, PEP_OUT_OF_MEMORY, "tpks");
   1.347 +        }
   1.348 +        tpks[tpks_count ++] = tpk;
   1.349 +    }
   1.350 +
   1.351 + out:
   1.352 +    if (status != PEP_STATUS_OK) {
   1.353 +        for (int i = 0; i < tpks_count; i ++)
   1.354 +            sq_tpk_free(tpks[i]);
   1.355 +        free(tpks);
   1.356 +    } else {
   1.357 +        *tpksp = tpks;
   1.358 +        *tpks_countp = tpks_count;
   1.359 +    }
   1.360 +
   1.361 +    T(" -> %s (%d tpks)", pep_status_to_string(status), *tpks_countp);
   1.362 +    return status;
   1.363 +}
   1.364 +
   1.365 +// Returns the TPK identified by the provided fingerprint.
   1.366 +//
   1.367 +// This function only matches on the primary key!
   1.368 +static PEP_STATUS tpk_find(PEP_SESSION, sq_fingerprint_t, int, sq_tpk_t *, int *)
   1.369      __attribute__((nonnull(1, 2)));
   1.370 -PEP_STATUS tsk_find_by_keyid(
   1.371 -        PEP_SESSION session, sq_keyid_t keyid, sq_tsk_t *tsk)
   1.372 +static PEP_STATUS tpk_find(PEP_SESSION session,
   1.373 +                           sq_fingerprint_t fpr, int private_only,
   1.374 +                           sq_tpk_t *tpk, int *secret)
   1.375 +{
   1.376 +    PEP_STATUS status = PEP_STATUS_OK;
   1.377 +    char *fpr_str = sq_fingerprint_to_hex(fpr);
   1.378 +
   1.379 +    T("(%s, %d)", fpr_str, private_only);
   1.380 +
   1.381 +    sqlite3_stmt *stmt = private_only ? session->tsk_find : session->tpk_find;
   1.382 +    sqlite3_bind_text(stmt, 1, fpr_str, -1, SQLITE_STATIC);
   1.383 +
   1.384 +    status = key_load(session, stmt, tpk, secret);
   1.385 +    ERROR_OUT(session, status, "Looking up %s", fpr_str);
   1.386 +
   1.387 + out:
   1.388 +    sqlite3_reset(stmt);
   1.389 +    T("(%s, %d) -> %s", fpr_str, private_only, pep_status_to_string(status));
   1.390 +    free(fpr_str);
   1.391 +    return status;
   1.392 +}
   1.393 +
   1.394 +// Returns the TPK identified by the provided keyid.
   1.395 +//
   1.396 +// This function matches on both primary keys and subkeys!
   1.397 +//
   1.398 +// Note: There can be multiple TPKs for a given keyid.  This can
   1.399 +// occur, because an encryption subkey can be bound to multiple TPKs.
   1.400 +// Also, it is possible to collide key ids.  If there are multiple key
   1.401 +// ids for a given key, this just returns one of them.
   1.402 +//
   1.403 +// If private_only is set, this will only consider TPKs with some
   1.404 +// secret key material.
   1.405 +static PEP_STATUS tpk_find_by_keyid_hex(PEP_SESSION, const char *, int, sq_tpk_t *, int *)
   1.406 +  __attribute__((nonnull(1, 2)));
   1.407 +static PEP_STATUS tpk_find_by_keyid_hex(
   1.408 +        PEP_SESSION session, const char *keyid_hex, int private_only,
   1.409 +        sq_tpk_t *tpkp, int *secretp)
   1.410 +{
   1.411 +    PEP_STATUS status = PEP_STATUS_OK;
   1.412 +    T("(%s, %d)", keyid_hex, private_only);
   1.413 +
   1.414 +    sqlite3_stmt *stmt
   1.415 +        = private_only ? session->tsk_find_by_keyid : session->tpk_find_by_keyid;
   1.416 +    sqlite3_bind_text(stmt, 1, keyid_hex, -1, SQLITE_STATIC);
   1.417 +
   1.418 +    status = key_load(session, stmt, tpkp, secretp);
   1.419 +    ERROR_OUT(session, status, "Looking up %s", keyid_hex);
   1.420 +
   1.421 + out:
   1.422 +    sqlite3_reset(stmt);
   1.423 +    T("(%s, %d) -> %s", keyid_hex, private_only, pep_status_to_string(status));
   1.424 +    return status;
   1.425 +}
   1.426 +
   1.427 +// See tpk_find_by_keyid_hex.
   1.428 +PEP_STATUS tpk_find_by_keyid(PEP_SESSION, sq_keyid_t, int, sq_tpk_t *, int *)
   1.429 +    __attribute__((nonnull(1, 2)));
   1.430 +PEP_STATUS tpk_find_by_keyid(PEP_SESSION session,
   1.431 +                             sq_keyid_t keyid, int private_only,
   1.432 +                             sq_tpk_t *tpkp, int *secretp)
   1.433  {
   1.434      char *keyid_hex = sq_keyid_to_hex(keyid);
   1.435      if (! keyid_hex)
   1.436          return PEP_OUT_OF_MEMORY;
   1.437 -    PEP_STATUS status = tsk_find_by_keyid_hex(session, keyid_hex, tsk);
   1.438 +    PEP_STATUS status
   1.439 +        = tpk_find_by_keyid_hex(session, keyid_hex, private_only, tpkp, secretp);
   1.440      free(keyid_hex);
   1.441      return status;
   1.442  }
   1.443  
   1.444 -// See tsk_find_by_keyid_hex.
   1.445 -static PEP_STATUS tsk_find_by_fpr(PEP_SESSION, sq_fingerprint_t, sq_tsk_t *)
   1.446 +// See tpk_find_by_keyid_hex.
   1.447 +static PEP_STATUS tpk_find_by_fpr(PEP_SESSION, sq_fingerprint_t, int,
   1.448 +                                  sq_tpk_t *, int *)
   1.449      __attribute__((nonnull(1, 2)));
   1.450 -static PEP_STATUS tsk_find_by_fpr(
   1.451 -        PEP_SESSION session, sq_fingerprint_t fpr, sq_tsk_t *tsk)
   1.452 +static PEP_STATUS tpk_find_by_fpr(
   1.453 +    PEP_SESSION session, sq_fingerprint_t fpr, int private_only,
   1.454 +    sq_tpk_t *tpkp, int *secretp)
   1.455  {
   1.456      sq_keyid_t keyid = sq_fingerprint_to_keyid(fpr);
   1.457      if (! keyid)
   1.458          return PEP_OUT_OF_MEMORY;
   1.459 -    PEP_STATUS status = tsk_find_by_keyid(session, keyid, tsk);
   1.460 +    PEP_STATUS status
   1.461 +        = tpk_find_by_keyid(session, keyid, private_only, tpkp, secretp);
   1.462      sq_keyid_free(keyid);
   1.463      return status;
   1.464  }
   1.465  
   1.466 -// See tsk_find_by_keyid_hex.
   1.467 -static PEP_STATUS tsk_find_by_fpr_hex(PEP_SESSION, const char *, sq_tsk_t *)
   1.468 +// See tpk_find_by_keyid_hex.
   1.469 +static PEP_STATUS tpk_find_by_fpr_hex(PEP_SESSION, const char *, int, sq_tpk_t *, int *secret)
   1.470      __attribute__((nonnull(1, 2)));
   1.471 -static PEP_STATUS tsk_find_by_fpr_hex(
   1.472 -        PEP_SESSION session, const char *fpr, sq_tsk_t *tsk)
   1.473 +static PEP_STATUS tpk_find_by_fpr_hex(
   1.474 +    PEP_SESSION session, const char *fpr, int private_only,
   1.475 +    sq_tpk_t *tpkp, int *secretp)
   1.476  {
   1.477      sq_fingerprint_t sq_fpr = sq_fingerprint_from_hex(fpr);
   1.478      if (! sq_fpr)
   1.479          return PEP_OUT_OF_MEMORY;
   1.480 -    PEP_STATUS status = tsk_find_by_fpr(session, sq_fpr, tsk);
   1.481 +    PEP_STATUS status
   1.482 +        = tpk_find_by_fpr(session, sq_fpr, private_only, tpkp, secretp);
   1.483      sq_fingerprint_free(sq_fpr);
   1.484      return status;
   1.485  }
   1.486  
   1.487 +// Returns all known TPKs.
   1.488 +static PEP_STATUS tpk_all(PEP_SESSION, int, sq_tpk_t **, int *) __attribute__((nonnull));
   1.489 +static PEP_STATUS tpk_all(PEP_SESSION session, int private_only,
   1.490 +                          sq_tpk_t **tpksp, int *tpks_countp) {
   1.491 +    PEP_STATUS status = PEP_STATUS_OK;
   1.492 +    sqlite3_stmt *stmt = private_only ? session->tsk_all : session->tpk_all;
   1.493 +    status = key_loadn(session, stmt, tpksp, tpks_countp);
   1.494 +    ERROR_OUT(session, status, "loading TPKs");
   1.495 + out:
   1.496 +    sqlite3_reset(stmt);
   1.497 +    return status;
   1.498 +}
   1.499  
   1.500 -// Saves the specified TSK.
   1.501 +// Returns keys that have a user id that matches the specified pattern.
   1.502  //
   1.503 -// This function takes ownership of TSK.
   1.504 -static PEP_STATUS tsk_save(PEP_SESSION, sq_tsk_t) __attribute__((nonnull));
   1.505 -static PEP_STATUS tsk_save(PEP_SESSION session, sq_tsk_t tsk)
   1.506 +// The keys returned must be freed using sq_tpk_free.
   1.507 +static PEP_STATUS tpk_find_by_email(PEP_SESSION, const char *, int, sq_tpk_t **, int *)
   1.508 +    __attribute__((nonnull));
   1.509 +static PEP_STATUS tpk_find_by_email(PEP_SESSION session,
   1.510 +                                    const char *pattern, int private_only,
   1.511 +                                    sq_tpk_t **tpksp, int *countp)
   1.512 +{
   1.513 +    PEP_STATUS status = PEP_STATUS_OK;
   1.514 +    T("(%s)", pattern);
   1.515 +
   1.516 +    sqlite3_stmt *stmt
   1.517 +        = private_only ? session->tsk_find_by_email : session->tpk_find_by_email;
   1.518 +    sqlite3_bind_text(stmt, 1, pattern, -1, SQLITE_STATIC);
   1.519 +
   1.520 +    status = key_loadn(session, stmt, tpksp, countp);
   1.521 +    ERROR_OUT(session, status, "Searching for '%s'", pattern);
   1.522 +
   1.523 + out:
   1.524 +    sqlite3_reset(stmt);
   1.525 +    T("(%s) -> %s (%d results)", pattern, pep_status_to_string(status), *countp);
   1.526 +    return status;
   1.527 +}
   1.528 +
   1.529 +
   1.530 +// Saves the specified TPK.
   1.531 +//
   1.532 +// This function takes ownership of TPK.
   1.533 +static PEP_STATUS tpk_save(PEP_SESSION, sq_tpk_t, identity_list **)
   1.534 +    __attribute__((nonnull(1, 2)));
   1.535 +static PEP_STATUS tpk_save(PEP_SESSION session, sq_tpk_t tpk,
   1.536 +                           identity_list **private_idents)
   1.537  {
   1.538      PEP_STATUS status = PEP_STATUS_OK;
   1.539      sq_fingerprint_t sq_fpr = NULL;
   1.540 @@ -403,37 +647,37 @@
   1.541      void *tsk_buffer = NULL;
   1.542      size_t tsk_buffer_len = 0;
   1.543      int tried_commit = 0;
   1.544 -    sq_tpk_t tpk = sq_tsk_tpk(tsk); /* Reference. */
   1.545      sq_tpk_key_iter_t key_iter = NULL;
   1.546 +    sq_user_id_binding_iter_t user_id_iter = NULL;
   1.547  
   1.548      sq_fpr = sq_tpk_fingerprint(tpk);
   1.549      fpr = sq_fingerprint_to_hex(sq_fpr);
   1.550 -    T("%s", fpr);
   1.551 +    T("(%s, private_idents: %s)", fpr, private_idents ? "yes" : "no");
   1.552  
   1.553 -    // Merge any existing data into TSK.
   1.554 -    sq_tsk_t current = NULL;
   1.555 -    status = tsk_find_by_fpr(session, sq_fpr, &current);
   1.556 +    // Merge any existing data into TPK.
   1.557 +    sq_tpk_t current = NULL;
   1.558 +    status = tpk_find(session, sq_fpr, false, &current, NULL);
   1.559      if (status == PEP_KEY_NOT_FOUND)
   1.560          status = PEP_STATUS_OK;
   1.561      else
   1.562          ERROR_OUT(session, status, "Looking up %s", fpr);
   1.563 -    if (current) {
   1.564 -        tpk = sq_tpk_merge(session->ctx,
   1.565 -                           sq_tsk_into_tpk(tsk), sq_tsk_into_tpk(current));
   1.566 -        tsk = sq_tpk_into_tsk(tpk);
   1.567 -        tpk = sq_tsk_tpk(tsk);
   1.568 -    }
   1.569 +    if (current)
   1.570 +        tpk = sq_tpk_merge(session->ctx, tpk, current);
   1.571  
   1.572 +    int is_tsk = sq_tpk_is_tsk(tpk);
   1.573  
   1.574      // Serialize it.
   1.575      sq_writer_t writer = sq_writer_alloc(&tsk_buffer, &tsk_buffer_len);
   1.576      if (! writer)
   1.577          ERROR_OUT(session, PEP_OUT_OF_MEMORY, "out of memory");
   1.578  
   1.579 -    sq_status_t sq_status = sq_tsk_serialize(session->ctx, tsk, writer);
   1.580 +    sq_status_t sq_status;
   1.581 +    sq_tsk_t tsk = sq_tpk_into_tsk(tpk);
   1.582 +    sq_status = sq_tsk_serialize(session->ctx, tsk, writer);
   1.583 +    tpk = sq_tsk_into_tpk(tsk);
   1.584      //sq_writer_free(writer);
   1.585      if (sq_status != 0)
   1.586 -        ERROR_OUT(session, PEP_UNKNOWN_ERROR, "Serializing TSK");
   1.587 +        ERROR_OUT(session, PEP_UNKNOWN_ERROR, "Serializing TPK");
   1.588  
   1.589  
   1.590      // Insert the TSK into the DB.
   1.591 @@ -445,25 +689,24 @@
   1.592                    "begin transaction failed: %s",
   1.593                    sqlite3_errmsg(session->key_db));
   1.594  
   1.595 -    stmt = session->tsk_save_insert_primary;
   1.596 +    stmt = session->tpk_save_insert_primary;
   1.597      sqlite3_bind_text(stmt, 1, fpr, -1, SQLITE_STATIC);
   1.598 -    sqlite3_bind_blob(stmt, 2, tsk_buffer, tsk_buffer_len, SQLITE_STATIC);
   1.599 +    sqlite3_bind_int(stmt, 2, is_tsk);
   1.600 +    sqlite3_bind_blob(stmt, 3, tsk_buffer, tsk_buffer_len, SQLITE_STATIC);
   1.601  
   1.602      sqlite_result = sqlite3_step(stmt);
   1.603      sqlite3_reset(stmt);
   1.604      if (sqlite_result != SQLITE_DONE)
   1.605          ERROR_OUT(session, PEP_UNKNOWN_ERROR,
   1.606 -                  "Saving TSK to DB: %s",
   1.607 -                  sqlite3_errmsg(session->key_db));
   1.608 +                  "Saving TPK: %s", sqlite3_errmsg(session->key_db));
   1.609  
   1.610      // Insert the "subkeys" (the primary key and the subkeys).
   1.611 -    stmt = session->tsk_save_insert_subkeys;
   1.612 +    stmt = session->tpk_save_insert_subkeys;
   1.613      key_iter = sq_tpk_key_iter(tpk);
   1.614      sq_p_key_t key;
   1.615      while ((key = sq_tpk_key_iter_next(key_iter, NULL, NULL))) {
   1.616          sq_keyid_t keyid = sq_p_key_keyid(key);
   1.617          char *keyid_hex = sq_keyid_to_hex(keyid);
   1.618 -        T("  subkey: %s", keyid_hex);
   1.619          sqlite3_bind_text(stmt, 1, keyid_hex, -1, SQLITE_STATIC);
   1.620          sqlite3_bind_text(stmt, 2, fpr, -1, SQLITE_STATIC);
   1.621  
   1.622 @@ -480,6 +723,63 @@
   1.623      sq_tpk_key_iter_free(key_iter);
   1.624      key_iter = NULL;
   1.625  
   1.626 +    // Insert the "userids".
   1.627 +    stmt = session->tpk_save_insert_userids;
   1.628 +    user_id_iter = sq_tpk_user_id_binding_iter(tpk);
   1.629 +    sq_user_id_binding_t binding;
   1.630 +    int first = 1;
   1.631 +    while ((binding = sq_user_id_binding_iter_next(user_id_iter))) {
   1.632 +        char *user_id = sq_user_id_binding_user_id(binding);
   1.633 +        if (!user_id || !*user_id)
   1.634 +            continue;
   1.635 +
   1.636 +        // Ignore bindings with a self-revocation certificate, but no
   1.637 +        // self-signature.
   1.638 +        if (!sq_user_id_binding_selfsig(binding)) {
   1.639 +            free(user_id);
   1.640 +            continue;
   1.641 +        }
   1.642 +
   1.643 +        char *name, *email;
   1.644 +        user_id_split(user_id, &name, &email); /* user_id is comsumed.  */
   1.645 +        // XXX: Correctly clean up name and email on error...
   1.646 +
   1.647 +        if (email) {
   1.648 +            T("  userid: %s", email);
   1.649 +
   1.650 +            sqlite3_bind_text(stmt, 1, email, -1, SQLITE_STATIC);
   1.651 +            sqlite3_bind_text(stmt, 2, fpr, -1, SQLITE_STATIC);
   1.652 +
   1.653 +            sqlite_result = sqlite3_step(stmt);
   1.654 +            sqlite3_reset(stmt);
   1.655 +
   1.656 +            if (sqlite_result != SQLITE_DONE) {
   1.657 +                sq_user_id_binding_iter_free(user_id_iter);
   1.658 +                free(name);
   1.659 +                ERROR_OUT(session, PEP_UNKNOWN_ERROR,
   1.660 +                          "Updating userids: %s", sqlite3_errmsg(session->key_db));
   1.661 +            }
   1.662 +        }
   1.663 +
   1.664 +        if (first && private_idents && is_tsk) {
   1.665 +            first = 0;
   1.666 +
   1.667 +            // Create an identity for the primary user id.
   1.668 +            pEp_identity *ident = new_identity(email, fpr, NULL, name);
   1.669 +            if (ident == NULL)
   1.670 +                ERROR_OUT(session, PEP_OUT_OF_MEMORY, "new_identity");
   1.671 +
   1.672 +            *private_idents = identity_list_add(*private_idents, ident);
   1.673 +            if (*private_idents == NULL)
   1.674 +                ERROR_OUT(session, PEP_OUT_OF_MEMORY, "identity_list_add");
   1.675 +        }
   1.676 +        free(email);
   1.677 +        free(name);
   1.678 +
   1.679 +    }
   1.680 +    sq_user_id_binding_iter_free(user_id_iter);
   1.681 +    user_id_iter = NULL;
   1.682 +
   1.683   out:
   1.684      // Prevent ERROR_OUT from causing an infinite loop.
   1.685      if (! tried_commit) {
   1.686 @@ -497,314 +797,21 @@
   1.687  
   1.688      T("(%s) -> %s", fpr, pep_status_to_string(status));
   1.689  
   1.690 +    if (user_id_iter)
   1.691 +        sq_user_id_binding_iter_free(user_id_iter);
   1.692      if (key_iter)
   1.693          sq_tpk_key_iter_free(key_iter);
   1.694      if (stmt)
   1.695        sqlite3_reset(stmt);
   1.696      free(tsk_buffer);
   1.697 -    sq_tsk_free(tsk);
   1.698 +    if (tpk)
   1.699 +        sq_tpk_free(tpk);
   1.700      free(fpr);
   1.701      sq_fingerprint_free(sq_fpr);
   1.702  
   1.703      return status;
   1.704  }
   1.705  
   1.706 -// Returns all known TSKs.
   1.707 -static PEP_STATUS tsk_all(PEP_SESSION, sq_tsk_t **, int *) __attribute__((nonnull));
   1.708 -static PEP_STATUS tsk_all(PEP_SESSION session, sq_tsk_t **tsksp, int *tsks_countp) {
   1.709 -    PEP_STATUS status = PEP_STATUS_OK;
   1.710 -
   1.711 -    int tsks_count = 0;
   1.712 -    int tsks_capacity = 8;
   1.713 -    sq_tsk_t *tsks = calloc(tsks_capacity, sizeof(sq_tsk_t));
   1.714 -    if (!tsks)
   1.715 -        ERROR_OUT(session, PEP_OUT_OF_MEMORY, "out of memory");
   1.716 -
   1.717 -    sqlite3_stmt *stmt = session->tsk_all;
   1.718 -    while (true) {
   1.719 -        switch (sqlite3_step(stmt)) {
   1.720 -        case SQLITE_ROW: {
   1.721 -            int data_len = sqlite3_column_bytes(stmt, 0);
   1.722 -            const void *data = sqlite3_column_blob(stmt, 0);
   1.723 -            sq_tpk_t tpk = sq_tpk_from_bytes(session->ctx, data, data_len);
   1.724 -            if (!tpk) {
   1.725 -                ERROR_OUT(session, PEP_GET_KEY_FAILED, "parsing TSK");
   1.726 -            } else {
   1.727 -                if (tsks_count == tsks_capacity) {
   1.728 -                    tsks_capacity *= 2;
   1.729 -                    tsks = realloc(tsks, sizeof(tsks[0]) * tsks_capacity);
   1.730 -                    if (!tsks)
   1.731 -                        ERROR_OUT(session, PEP_OUT_OF_MEMORY, "tsks");
   1.732 -                }
   1.733 -                tsks[tsks_count ++] = sq_tpk_into_tsk(tpk);
   1.734 -            }
   1.735 -            break;
   1.736 -        }
   1.737 -        default:
   1.738 -            ERROR_OUT(session, PEP_UNKNOWN_ERROR,
   1.739 -                      "stepping sqlite statement: %s",
   1.740 -                      sqlite3_errmsg(session->key_db));
   1.741 -        case SQLITE_DONE:
   1.742 -            goto out;
   1.743 -        }
   1.744 -    }
   1.745 -
   1.746 - out:
   1.747 -    sqlite3_reset(stmt);
   1.748 -
   1.749 -    if (status != PEP_STATUS_OK) {
   1.750 -        for (int i = 0; i < tsks_count; i ++)
   1.751 -            sq_tsk_free(tsks[i]);
   1.752 -        free(tsks);
   1.753 -    } else {
   1.754 -        *tsksp = tsks;
   1.755 -        *tsks_countp = tsks_count;
   1.756 -    }
   1.757 -
   1.758 -    return status;
   1.759 -}
   1.760 -
   1.761 -// Returns the key with the label LABEL.
   1.762 -//
   1.763 -// The return is returned in *KEY and must be freed using sq_tpk_free.
   1.764 -//
   1.765 -// Note: we maintain labels for the fingerprint of primary keys, pep
   1.766 -// user ids, and email addresses.  If you want to look something up by
   1.767 -// subkey id, use tpk_find_by_keyid.
   1.768 -static PEP_STATUS tpk_find_by_label(PEP_SESSION, const char *, sq_tpk_t *)
   1.769 -    __attribute__((nonnull));
   1.770 -static PEP_STATUS tpk_find_by_label(PEP_SESSION session, const char *label, sq_tpk_t *tpk)
   1.771 -{
   1.772 -    PEP_STATUS status = PEP_STATUS_OK;
   1.773 -
   1.774 -    sq_binding_t binding
   1.775 -        = sq_store_lookup(session->ctx, session->store, label);
   1.776 -    if (!binding)
   1.777 -        ERROR_OUT(session, PEP_KEY_NOT_FOUND, "looking up label %s", label);
   1.778 -
   1.779 -    *tpk = sq_binding_tpk(session->ctx, binding);
   1.780 -    if (!*tpk)
   1.781 -        ERROR_OUT(session, PEP_GET_KEY_FAILED, "getting TPK");
   1.782 -
   1.783 - out:
   1.784 -    if (binding)
   1.785 -        sq_binding_free(binding);
   1.786 -
   1.787 -    return status;
   1.788 -}
   1.789 -
   1.790 -// Returns the key with keyid KEYID.
   1.791 -//
   1.792 -// Note: this function will match both the primary key as well as any
   1.793 -// subkeys.
   1.794 -static PEP_STATUS tpk_find_by_keyid(PEP_SESSION, sq_keyid_t, sq_tpk_t *)
   1.795 -    __attribute__((nonnull));
   1.796 -static PEP_STATUS tpk_find_by_keyid(PEP_SESSION session, sq_keyid_t keyid,
   1.797 -                                    sq_tpk_t *tpk)
   1.798 -{
   1.799 -    PEP_STATUS status = PEP_STATUS_OK;
   1.800 -    char *keyid_hex = sq_keyid_to_hex(keyid);
   1.801 -
   1.802 -    sq_key_t key = sq_store_lookup_by_subkeyid(session->ctx, keyid);
   1.803 -    if (!key)
   1.804 -        ERROR_OUT(session, PEP_KEY_NOT_FOUND,
   1.805 -                  "looking up key by keyid %s", keyid_hex);
   1.806 -
   1.807 -    *tpk = sq_key_tpk(session->ctx, key);
   1.808 -    if (!*tpk)
   1.809 -        ERROR_OUT(session, PEP_GET_KEY_FAILED, "getting TPK");
   1.810 -
   1.811 - out:
   1.812 -    free(keyid_hex);
   1.813 -
   1.814 -    return status;
   1.815 -}
   1.816 -
   1.817 -// Returns the key with fingerprint FPR.
   1.818 -//
   1.819 -// Note: this function will match both the primary key as well as any
   1.820 -// subkeys.
   1.821 -static PEP_STATUS tpk_find_by_fpr(PEP_SESSION, sq_fingerprint_t, sq_tpk_t *)
   1.822 -    __attribute__((nonnull));
   1.823 -static PEP_STATUS tpk_find_by_fpr(PEP_SESSION session, sq_fingerprint_t fpr,
   1.824 -                                  sq_tpk_t *tpk)
   1.825 -{
   1.826 -    sq_keyid_t keyid = sq_fingerprint_to_keyid(fpr);
   1.827 -    PEP_STATUS status = tpk_find_by_keyid(session, keyid, tpk);
   1.828 -    sq_keyid_free(keyid);
   1.829 -    return status;
   1.830 -}
   1.831 -
   1.832 -
   1.833 -
   1.834 -// Saves a TPK.
   1.835 -//
   1.836 -// Creates labels under the fingerprint, address (if not NULL), and
   1.837 -// the email address in each user id.
   1.838 -//
   1.839 -// If there are any keys with private key material, saves that
   1.840 -// information in private_idents (if not NULL).
   1.841 -//
   1.842 -// This function takes ownership of the tpk.
   1.843 -static PEP_STATUS tpk_save(PEP_SESSION, sq_tpk_t, const char *,
   1.844 -                           identity_list **, int)
   1.845 -  __attribute__((nonnull(1, 2)));
   1.846 -static PEP_STATUS tpk_save(PEP_SESSION session, sq_tpk_t tpk,
   1.847 -                           const char *address, identity_list **private_idents,
   1.848 -                           int replace_bindings)
   1.849 -{
   1.850 -    PEP_STATUS status = PEP_STATUS_OK;
   1.851 -    sq_user_id_binding_iter_t iter = NULL;
   1.852 -    sq_user_id_binding_t user_id_binding = NULL;
   1.853 -
   1.854 -    if (private_idents)
   1.855 -        *private_idents = NULL;
   1.856 -
   1.857 -    sq_fingerprint_t sq_fpr = sq_tpk_fingerprint(tpk);
   1.858 -    char *fpr = sq_fingerprint_to_hex(sq_fpr);
   1.859 -    T("(%s)", fpr);
   1.860 -
   1.861 -    // Import the public part in the store.  If it was already present
   1.862 -    // in the store, it will be merged.  We don't work with the merged
   1.863 -    // TPK, because we only care about new user ids.
   1.864 -    sq_tpk_t merged = sq_store_import(session->ctx, session->store, fpr, tpk);
   1.865 -    if (! merged)
   1.866 -        ERROR_OUT(session, PEP_UNKNOWN_ERROR, "Merging TPK (%s)", fpr);
   1.867 -    sq_tpk_free(merged);
   1.868 -
   1.869 -    // Add the pep user id label.
   1.870 -    if (address) {
   1.871 -        int first_try = 1;
   1.872 -        sq_binding_t binding;
   1.873 -
   1.874 -    make_address_binding:
   1.875 -        binding = sq_store_add(session->ctx, session->store, address, sq_fpr);
   1.876 -        if (! binding) {
   1.877 -            // An error occured.  There's a good chance this is
   1.878 -            // because the binding already exists.
   1.879 -            if (replace_bindings && first_try) {
   1.880 -                T("replacing userid binding %s -> %s", address, fpr);
   1.881 -
   1.882 -                // We should replace the existing binding.
   1.883 -                binding = sq_store_lookup(session->ctx, session->store, address);
   1.884 -                if (binding) {
   1.885 -                    if (sq_binding_delete(session->ctx, binding)) {
   1.886 -                        DUMP_ERR(session, PEP_STATUS_OK,
   1.887 -                                 "Delete binding %s", address);
   1.888 -                        sq_binding_free(binding);
   1.889 -                    }
   1.890 -
   1.891 -                    first_try = 0;
   1.892 -                    goto make_address_binding;
   1.893 -                }
   1.894 -            }
   1.895 -
   1.896 -            // This is a soft error: we always prefer the existing
   1.897 -            // binding.
   1.898 -            DUMP_ERR(session, PEP_STATUS_OK,
   1.899 -                     "Creating userid binding %s -> %s", address, fpr);
   1.900 -        } else {
   1.901 -            sq_binding_free(binding);
   1.902 -        }
   1.903 -    }
   1.904 -
   1.905 -    // Create a binding for each user id.
   1.906 -    //
   1.907 -    // Note: the iterator only returns valid user ids in the sense
   1.908 -    // that the user id has a self-signature or a self-revocation
   1.909 -    // certificate.
   1.910 -    int first = 1;
   1.911 -    iter = sq_tpk_user_id_binding_iter(tpk);
   1.912 -    while ((user_id_binding = sq_user_id_binding_iter_next(iter))) {
   1.913 -        char *user_id = sq_user_id_binding_user_id(user_id_binding);
   1.914 -        if (!user_id) {
   1.915 -            // Completely ignore insane user ids (those with interior
   1.916 -            // NUL bytes).
   1.917 -            free(user_id);
   1.918 -            continue;
   1.919 -        }
   1.920 -
   1.921 -        // Ignore bindings with a self-revocation certificate, but no
   1.922 -        // self-signature.
   1.923 -        if (!sq_user_id_binding_selfsig(user_id_binding)) {
   1.924 -            free(user_id);
   1.925 -            continue;
   1.926 -        }
   1.927 -
   1.928 -        char *name, *email;
   1.929 -        user_id_split(user_id, &name, &email); /* user_id is comsumed.  */
   1.930 -
   1.931 -        if (email) {
   1.932 -            int first_try = 1;
   1.933 -            sq_binding_t binding;
   1.934 -
   1.935 -        make_email_binding:
   1.936 -            binding = sq_store_add(session->ctx, session->store, email, sq_fpr);
   1.937 -            if (! binding) {
   1.938 -                // An error occured.  There's a good chance this is
   1.939 -                // because the binding already exists.
   1.940 -                if (replace_bindings && first_try) {
   1.941 -                    // We should replace the existing binding.
   1.942 -                    binding = sq_store_lookup(session->ctx, session->store, email);
   1.943 -                    if (binding) {
   1.944 -                        if (sq_binding_delete(session->ctx, binding)) {
   1.945 -                            DUMP_ERR(session, PEP_STATUS_OK,
   1.946 -                                     "Delete binding %s", email);
   1.947 -                            sq_binding_free(binding);
   1.948 -                        }
   1.949 -
   1.950 -                        first_try = 0;
   1.951 -                        goto make_email_binding;
   1.952 -                    }
   1.953 -                }
   1.954 -
   1.955 -                // This is a soft error: we always prefer the existing
   1.956 -                // binding.
   1.957 -                DUMP_ERR(session, PEP_UNKNOWN_ERROR,
   1.958 -                         "Creating email binding: %s -> %s", email, fpr);
   1.959 -            } else {
   1.960 -                sq_binding_free(binding);
   1.961 -            }
   1.962 -
   1.963 -            if (first && private_idents && sq_tpk_is_tsk(tpk)) {
   1.964 -                first = 0;
   1.965 -
   1.966 -                // Create an identity for the primary user id.
   1.967 -                pEp_identity *ident = new_identity(email, fpr, NULL, name);
   1.968 -                if (ident == NULL)
   1.969 -                    ERROR_OUT(session, PEP_OUT_OF_MEMORY, "new_identity");
   1.970 -
   1.971 -                *private_idents = identity_list_add(*private_idents, ident);
   1.972 -                if (*private_idents == NULL)
   1.973 -                    ERROR_OUT(session, PEP_OUT_OF_MEMORY, "identity_list_add");
   1.974 -            }
   1.975 -        }
   1.976 -    }
   1.977 -
   1.978 -    sq_user_id_binding_iter_free(iter);
   1.979 -    iter = NULL;
   1.980 -
   1.981 -    // If it has any private key material, save it in the TSK store.
   1.982 -    if (sq_tpk_is_tsk(tpk)) {
   1.983 -        status = tsk_save(session, sq_tpk_into_tsk(tpk));
   1.984 -        tpk = NULL;
   1.985 -        ERROR_OUT(session, status, "Saving TSK");
   1.986 -    }
   1.987 -
   1.988 - out:
   1.989 -    T("(%s) -> %s", fpr, pep_status_to_string(status));
   1.990 -
   1.991 -    if (iter)
   1.992 -        sq_user_id_binding_iter_free(iter);
   1.993 -    free(fpr);
   1.994 -    if (sq_fpr)
   1.995 -        sq_fingerprint_free(sq_fpr);
   1.996 -    if (tpk)
   1.997 -        sq_tpk_free(tpk);
   1.998 -
   1.999 -    return status;
  1.1000 -}
  1.1001 -
  1.1002  struct decrypt_cookie {
  1.1003      PEP_SESSION session;
  1.1004      int get_secret_keys_called;
  1.1005 @@ -834,7 +841,8 @@
  1.1006      j = 0;
  1.1007      for (i = 0; i < keyids_len; i ++) {
  1.1008          sq_tpk_t tpk = NULL;
  1.1009 -        sq_status_t status = tpk_find_by_keyid(session, keyids[i], &tpk);
  1.1010 +        sq_status_t status
  1.1011 +            = tpk_find_by_keyid(session, keyids[i], false, &tpk, NULL);
  1.1012          if (status == SQ_STATUS_SUCCESS)
  1.1013              (*tpks)[j ++] = tpk;
  1.1014      }
  1.1015 @@ -850,7 +858,7 @@
  1.1016  {
  1.1017      struct decrypt_cookie *cookie = cookie_opaque;
  1.1018      PEP_SESSION session = cookie->session;
  1.1019 -    sq_tsk_t *tsks = NULL;
  1.1020 +    sq_tpk_t *tsks = NULL;
  1.1021      int tsks_count = 0;
  1.1022      int wildcards = 0;
  1.1023  
  1.1024 @@ -879,30 +887,24 @@
  1.1025          // Collect the recipients.  Note: we must return the primary
  1.1026          // key's fingerprint.
  1.1027          sq_tpk_t tpk = NULL;
  1.1028 -        if (tpk_find_by_keyid(session, keyid, &tpk) == PEP_STATUS_OK) {
  1.1029 -            sq_fingerprint_t fp = sq_tpk_fingerprint(tpk);
  1.1030 -            char *fp_string = sq_fingerprint_to_hex(fp);
  1.1031 -            stringlist_add_unique(cookie->recipient_keylist, fp_string);
  1.1032 -            free(fp_string);
  1.1033 -            sq_fingerprint_free(fp);
  1.1034 -            sq_tpk_free(tpk);
  1.1035 -        }
  1.1036 +        int is_tsk = 0;
  1.1037 +        if (tpk_find_by_keyid(session, keyid, false, &tpk, &is_tsk) != PEP_STATUS_OK)
  1.1038 +            goto eol;
  1.1039 +
  1.1040 +        sq_fingerprint_t fp = sq_tpk_fingerprint(tpk);
  1.1041 +        char *fp_string = sq_fingerprint_to_hex(fp);
  1.1042 +        stringlist_add_unique(cookie->recipient_keylist, fp_string);
  1.1043 +        free(fp_string);
  1.1044 +        sq_fingerprint_free(fp);
  1.1045  
  1.1046          if (cookie->decrypted)
  1.1047              goto eol;
  1.1048  
  1.1049          // See if we have the secret key.
  1.1050 -        sq_tsk_t tsk = NULL;
  1.1051 -        PEP_STATUS s = tsk_find_by_keyid(cookie->session, keyid, &tsk);
  1.1052 -        if (s != PEP_STATUS_OK) {
  1.1053 -            if (s != PEP_KEY_NOT_FOUND)
  1.1054 -                DUMP_ERR(cookie->session, s, "Parsing key %s", keyid_str);
  1.1055 -            else
  1.1056 -                T("No secret key material for %s", keyid_str);
  1.1057 +        assert(is_tsk == sq_tpk_is_tsk(tpk));
  1.1058 +        if (! is_tsk)
  1.1059              goto eol;
  1.1060 -        }
  1.1061  
  1.1062 -        tpk = sq_tsk_tpk(tsk);
  1.1063          key_iter = sq_tpk_key_iter(tpk);
  1.1064          sq_p_key_t key;
  1.1065          while ((key = sq_tpk_key_iter_next(key_iter, NULL, NULL))) {
  1.1066 @@ -916,8 +918,10 @@
  1.1067                  break;
  1.1068          }
  1.1069  
  1.1070 -        if (key == NULL)
  1.1071 +        if (key == NULL) {
  1.1072              assert(!"Inconsistent DB: key doesn't contain a subkey with keyid!");
  1.1073 +            goto eol;
  1.1074 +        }
  1.1075  
  1.1076          uint8_t algo;
  1.1077          uint8_t session_key[1024];
  1.1078 @@ -938,6 +942,8 @@
  1.1079          free(keyid_str);
  1.1080          if (key_iter)
  1.1081              sq_tpk_key_iter_free(key_iter);
  1.1082 +        if (tpk)
  1.1083 +            sq_tpk_free(tpk);
  1.1084      }
  1.1085  
  1.1086      // Consider wildcard recipients.
  1.1087 @@ -951,15 +957,15 @@
  1.1088              goto eol2;
  1.1089  
  1.1090          if (!tsks) {
  1.1091 -            if (tsk_all(session, &tsks, &tsks_count) != PEP_STATUS_OK) {
  1.1092 +            if (tpk_all(session, true, &tsks, &tsks_count) != PEP_STATUS_OK) {
  1.1093                  DUMP_ERR(session, PEP_UNKNOWN_ERROR, "Getting all tsks");
  1.1094              }
  1.1095          }
  1.1096  
  1.1097          for (int j = 0; j < tsks_count; j ++) {
  1.1098 -            sq_tpk_t tpk = sq_tsk_tpk(tsks[j]);
  1.1099 +            sq_tpk_t tsk = tsks[j];
  1.1100  
  1.1101 -            key_iter = sq_tpk_key_iter(tpk);
  1.1102 +            key_iter = sq_tpk_key_iter(tsk);
  1.1103              sq_p_key_t key;
  1.1104              sq_signature_t selfsig;
  1.1105              while ((key = sq_tpk_key_iter_next(key_iter, &selfsig, NULL))) {
  1.1106 @@ -979,7 +985,7 @@
  1.1107                      continue;
  1.1108  
  1.1109                  // Add it to the recipient list.
  1.1110 -                sq_fingerprint_t fp = sq_tpk_fingerprint(tpk);
  1.1111 +                sq_fingerprint_t fp = sq_tpk_fingerprint(tsk);
  1.1112                  char *fp_string = sq_fingerprint_to_hex(fp);
  1.1113                  T("wildcard recipient appears to be %s", fp_string);
  1.1114                  stringlist_add_unique(cookie->recipient_keylist, fp_string);
  1.1115 @@ -1001,7 +1007,7 @@
  1.1116  
  1.1117      if (tsks) {
  1.1118          for (int i = 0; i < tsks_count; i ++)
  1.1119 -            sq_tsk_free(tsks[i]);
  1.1120 +            sq_tpk_free(tsks[i]);
  1.1121          free(tsks);
  1.1122      }
  1.1123  
  1.1124 @@ -1046,7 +1052,7 @@
  1.1125              sq_fingerprint_t issuer_fp = sq_signature_issuer_fingerprint(sig);
  1.1126              if (issuer_fp) {
  1.1127                  sq_keyid_t issuer = sq_fingerprint_to_keyid(issuer_fp);
  1.1128 -                if (tpk_find_by_keyid(session, issuer, &tpk) != PEP_STATUS_OK)
  1.1129 +                if (tpk_find_by_keyid(session, issuer, false, &tpk, NULL) != PEP_STATUS_OK)
  1.1130                      ; // Soft error.  Ignore.
  1.1131                  sq_keyid_free(issuer);
  1.1132                  sq_fingerprint_free(issuer_fp);
  1.1133 @@ -1056,7 +1062,7 @@
  1.1134              if (!tpk) {
  1.1135                  sq_keyid_t issuer = sq_signature_issuer(sig);
  1.1136                  if (issuer) {
  1.1137 -                    if (tpk_find_by_keyid(session, issuer, &tpk) != PEP_STATUS_OK)
  1.1138 +                    if (tpk_find_by_keyid(session, issuer, false, &tpk, NULL) != PEP_STATUS_OK)
  1.1139                          ; // Soft error.  Ignore.
  1.1140                  }
  1.1141                  sq_keyid_free(issuer);
  1.1142 @@ -1268,13 +1274,11 @@
  1.1143      assert(ssize);
  1.1144  
  1.1145      PEP_STATUS status = PEP_STATUS_OK;
  1.1146 -    sq_tsk_t signer = NULL;
  1.1147 -    sq_tpk_t signer_tpk = NULL; /* Reference.  */
  1.1148 +    sq_tpk_t signer = NULL;
  1.1149      sq_writer_stack_t ws = NULL;
  1.1150  
  1.1151 -    status = tsk_find_by_fpr_hex(session, fpr, &signer);
  1.1152 +    status = tpk_find_by_fpr_hex(session, fpr, true, &signer, NULL);
  1.1153      ERROR_OUT(session, status, "Looking up key '%s'", fpr);
  1.1154 -    signer_tpk = sq_tsk_tpk(signer);
  1.1155  
  1.1156      sq_writer_t writer = sq_writer_alloc((void **) stext, ssize);
  1.1157      writer = sq_armor_writer_new(session->ctx, writer,
  1.1158 @@ -1284,7 +1288,7 @@
  1.1159  
  1.1160      ws = sq_writer_stack_message(writer);
  1.1161  
  1.1162 -    ws = sq_signer_new_detached(session->ctx, ws, &signer_tpk, 1);
  1.1163 +    ws = sq_signer_new_detached(session->ctx, ws, &signer, 1);
  1.1164      if (!ws)
  1.1165          ERROR_OUT(session, PEP_UNKNOWN_ERROR, "Setting up signer");
  1.1166  
  1.1167 @@ -1310,7 +1314,7 @@
  1.1168      }
  1.1169  
  1.1170      if (signer)
  1.1171 -        sq_tsk_free(signer);
  1.1172 +        sq_tpk_free(signer);
  1.1173  
  1.1174      T("(%s)-> %s", fpr, pep_status_to_string(status));
  1.1175      return status;
  1.1176 @@ -1323,8 +1327,7 @@
  1.1177      PEP_STATUS status = PEP_STATUS_OK;
  1.1178      int keys_count = 0;
  1.1179      sq_tpk_t *keys = NULL;
  1.1180 -    sq_tsk_t signer = NULL;
  1.1181 -    sq_tpk_t signer_tpk = NULL; /* Reference. */
  1.1182 +    sq_tpk_t signer = NULL;
  1.1183      sq_writer_stack_t ws = NULL;
  1.1184  
  1.1185      assert(session);
  1.1186 @@ -1346,16 +1349,15 @@
  1.1187      for (_keylist = keylist; _keylist != NULL; _keylist = _keylist->next) {
  1.1188          assert(_keylist->value);
  1.1189          sq_fingerprint_t sq_fpr = sq_fingerprint_from_hex(_keylist->value);
  1.1190 -        status = tpk_find_by_fpr(session, sq_fpr, &keys[keys_count ++]);
  1.1191 +        status = tpk_find_by_fpr(session, sq_fpr, false, &keys[keys_count ++], NULL);
  1.1192          sq_fingerprint_free(sq_fpr);
  1.1193 -        ERROR_OUT(session, status, "Looking up key '%s'", _keylist->value);
  1.1194 +        ERROR_OUT(session, status, "Looking up key for recipient '%s'", _keylist->value);
  1.1195      }
  1.1196  
  1.1197      if (sign) {
  1.1198          // The first key in the keylist is the signer.
  1.1199 -        status = tsk_find_by_fpr_hex(session, keylist->value, &signer);
  1.1200 -        ERROR_OUT(session, status, "Looking up key '%s'", keylist->value);
  1.1201 -        signer_tpk = sq_tsk_tpk(signer);
  1.1202 +        status = tpk_find_by_fpr_hex(session, keylist->value, true, &signer, NULL);
  1.1203 +        ERROR_OUT(session, status, "Looking up key for signing '%s'", keylist->value);
  1.1204      }
  1.1205  
  1.1206      sq_writer_t writer = sq_writer_alloc((void **) ctext, csize);
  1.1207 @@ -1374,7 +1376,7 @@
  1.1208      }
  1.1209  
  1.1210      if (sign) {
  1.1211 -        ws = sq_signer_new(session->ctx, ws, &signer_tpk, 1);
  1.1212 +        ws = sq_signer_new(session->ctx, ws, &signer, 1);
  1.1213          if (!ws)
  1.1214              ERROR_OUT(session, PEP_UNKNOWN_ERROR, "Setting up signer");
  1.1215      }
  1.1216 @@ -1405,7 +1407,7 @@
  1.1217      }
  1.1218  
  1.1219      if (signer)
  1.1220 -        sq_tsk_free(signer);
  1.1221 +        sq_tpk_free(signer);
  1.1222      for (int i = 0; i < keys_count; i ++)
  1.1223          sq_tpk_free(keys[i]);
  1.1224      free(keys);
  1.1225 @@ -1449,6 +1451,8 @@
  1.1226      if (! userid)
  1.1227          ERROR_OUT(session, PEP_OUT_OF_MEMORY, "asprintf");
  1.1228  
  1.1229 +    T("(%s)", userid);
  1.1230 +
  1.1231      // Generate a key.
  1.1232      sq_tsk_t tsk;
  1.1233      sq_signature_t rev;
  1.1234 @@ -1464,7 +1468,7 @@
  1.1235      sq_fpr = sq_tpk_fingerprint(tpk);
  1.1236      fpr = sq_fingerprint_to_hex(sq_fpr);
  1.1237  
  1.1238 -    status = tpk_save(session, tpk, identity->address, NULL, 1);
  1.1239 +    status = tpk_save(session, tpk, NULL);
  1.1240      tpk = NULL;
  1.1241      if (status != 0)
  1.1242          ERROR_OUT(session, PEP_CANNOT_CREATE_KEY, "saving TSK");
  1.1243 @@ -1534,7 +1538,7 @@
  1.1244  
  1.1245          // If private_idents is not NULL and there is any private key
  1.1246          // material, it will be saved.
  1.1247 -        status = tpk_save(session, tpk, NULL, private_idents, false);
  1.1248 +        status = tpk_save(session, tpk, private_idents);
  1.1249          ERROR_OUT(session, status, "saving TPK");
  1.1250  
  1.1251          break;
  1.1252 @@ -1569,19 +1573,14 @@
  1.1253      T("(%s, %s)", fpr, secret ? "secret" : "public");
  1.1254  
  1.1255      if (secret) {
  1.1256 -        sq_tsk_t tsk;
  1.1257 -        status = tsk_find_by_fpr_hex(session, fpr, &tsk);
  1.1258 -        if (status == PEP_KEY_NOT_FOUND) {
  1.1259 +        status = tpk_find_by_fpr_hex(session, fpr, true, &secret_key, NULL);
  1.1260 +        if (status == PEP_KEY_NOT_FOUND)
  1.1261              status = PEP_STATUS_OK;
  1.1262 -        } else if (status == PEP_STATUS_OK) {
  1.1263 -            secret_key = sq_tsk_into_tpk(tsk);
  1.1264 -        } else {
  1.1265 -            ERROR_OUT(session, status, "Looking up TSK");
  1.1266 -        }
  1.1267 +        ERROR_OUT(session, status, "Looking up TSK for %s", fpr);
  1.1268      }
  1.1269  
  1.1270      sq_fingerprint_t sq_fpr = sq_fingerprint_from_hex(fpr);
  1.1271 -    status = tpk_find_by_fpr(session, sq_fpr, &tpk);
  1.1272 +    status = tpk_find_by_fpr(session, sq_fpr, false, &tpk, NULL);
  1.1273      sq_fingerprint_free(sq_fpr);
  1.1274      ERROR_OUT(session, status, "Looking up TPK for %s", fpr);
  1.1275  
  1.1276 @@ -1628,121 +1627,6 @@
  1.1277      return status;
  1.1278  }
  1.1279  
  1.1280 -static stringpair_list_t *add_key(PEP_SESSION session, stringpair_list_t *k,
  1.1281 -                                  sq_tpk_t tpk, sq_fingerprint_t fpr) {
  1.1282 -    sq_revocation_status_t rs = sq_tpk_revocation_status(tpk);
  1.1283 -    sq_revocation_status_variant_t rsv = sq_revocation_status_variant(rs);
  1.1284 -    sq_revocation_status_free(rs);
  1.1285 -    if (rsv == SQ_REVOCATION_STATUS_REVOKED)
  1.1286 -        return k;
  1.1287 -
  1.1288 -    int dealloc_fpr = 0;
  1.1289 -    if (!fpr) {
  1.1290 -        dealloc_fpr = 1;
  1.1291 -        fpr = sq_tpk_fingerprint(tpk);
  1.1292 -    }
  1.1293 -
  1.1294 -    char *fpr_str = sq_fingerprint_to_hex(fpr);
  1.1295 -    char *user_id = sq_tpk_primary_user_id(tpk);
  1.1296 -    if (user_id) {
  1.1297 -        T("  %s -> %s", fpr_str, user_id);
  1.1298 -        k = stringpair_list_add(k, new_stringpair(fpr_str, user_id));
  1.1299 -    }
  1.1300 -
  1.1301 -    free(user_id);
  1.1302 -    free(fpr_str);
  1.1303 -    if (dealloc_fpr)
  1.1304 -        sq_fingerprint_free(fpr);
  1.1305 -
  1.1306 -    return k;
  1.1307 -}
  1.1308 -
  1.1309 -// pattern could be empty, an fpr, or a mailbox.
  1.1310 -//
  1.1311 -// keyinfo_list is a list of <fpr, openpgp userid> tuples for the
  1.1312 -// matching keys.
  1.1313 -//
  1.1314 -// This function filters out revoked key, but not expired keys.
  1.1315 -PEP_STATUS pgp_list_keyinfo(PEP_SESSION session,
  1.1316 -                            const char* pattern,
  1.1317 -                            stringpair_list_t** keyinfo_list)
  1.1318 -{
  1.1319 -    PEP_STATUS status = PEP_STATUS_OK;
  1.1320 -    sq_tpk_t tpk = NULL;
  1.1321 -    sq_fingerprint_t fpr = NULL;
  1.1322 -
  1.1323 -    T("('%s')", pattern);
  1.1324 -
  1.1325 -    *keyinfo_list = new_stringpair_list(NULL);
  1.1326 -    if (!*keyinfo_list)
  1.1327 -        ERROR_OUT(session, PEP_OUT_OF_MEMORY, "new_stringlist");
  1.1328 -
  1.1329 -    // Trim any leading space.  This also makes it easier to recognize
  1.1330 -    // a string that is only whitespace.
  1.1331 -    while (*pattern == ' ')
  1.1332 -        pattern ++;
  1.1333 -
  1.1334 -    if (strchr(pattern, '@')) {
  1.1335 -        // Looks like a mailbox.
  1.1336 -        status = tpk_find_by_label(session, pattern, &tpk);
  1.1337 -        ERROR_OUT(session, status, "Looking up '%s'", pattern);
  1.1338 -        add_key(session, *keyinfo_list, tpk, NULL);
  1.1339 -
  1.1340 -        assert(!"pgp_list_keyinfo(email) untested, please make a test case");
  1.1341 -    } else if (// Only hex characters and spaces
  1.1342 -               pattern[strspn(pattern, "0123456789aAbBcCdDeEfF ")] == 0
  1.1343 -               // And a fair amount of them.
  1.1344 -               && strlen(pattern) >= 16) {
  1.1345 -        // Fingerprint.
  1.1346 -        fpr = sq_fingerprint_from_hex(pattern);
  1.1347 -        status = tpk_find_by_fpr(session, fpr, &tpk);
  1.1348 -        ERROR_OUT(session, status, "Looking up key");
  1.1349 -        add_key(session, *keyinfo_list, tpk, fpr);
  1.1350 -
  1.1351 -        assert(!"pgp_list_keyinfo(fpr) untested, please make a test case");
  1.1352 -    } else if (pattern[0] == 0) {
  1.1353 -        // Empty string.
  1.1354 -        sq_binding_iter_t iter = sq_store_iter(session->ctx, session->store);
  1.1355 -        sq_binding_t binding;
  1.1356 -        char *label;
  1.1357 -        stringpair_list_t *_k = *keyinfo_list;
  1.1358 -        while ((binding = sq_binding_iter_next(iter, &label, &fpr))) {
  1.1359 -            if (strchr(label, '@')) {
  1.1360 -                char *fpr_str = sq_fingerprint_to_hex(fpr);
  1.1361 -                T("  %s -> %s", fpr_str, label);
  1.1362 -                _k = stringpair_list_add(_k, new_stringpair(fpr_str, label));
  1.1363 -                free(fpr_str);
  1.1364 -            }
  1.1365 -            free(label);
  1.1366 -            sq_fingerprint_free(fpr);
  1.1367 -            fpr = NULL;
  1.1368 -            sq_binding_free(binding);
  1.1369 -        }
  1.1370 -        sq_binding_iter_free(iter);
  1.1371 -    }
  1.1372 -
  1.1373 - out:
  1.1374 -    if (tpk)
  1.1375 -        sq_tpk_free(tpk);
  1.1376 -    if (fpr)
  1.1377 -        sq_fingerprint_free(fpr);
  1.1378 -    if (status != PEP_STATUS_OK && *keyinfo_list) {
  1.1379 -        free_stringpair_list(*keyinfo_list);
  1.1380 -        *keyinfo_list = NULL;
  1.1381 -    }
  1.1382 -    if (status == PEP_KEY_NOT_FOUND)
  1.1383 -        status = PEP_STATUS_OK;
  1.1384 -
  1.1385 -    T("(%s) -> %s", pattern, pep_status_to_string(status));
  1.1386 -    return status;
  1.1387 -}
  1.1388 -
  1.1389 -PEP_STATUS pgp_recv_key(PEP_SESSION session, const char *pattern)
  1.1390 -{
  1.1391 -    assert(!"pgp_recv_key not implemented");
  1.1392 -    return PEP_UNKNOWN_ERROR;
  1.1393 -}
  1.1394 -
  1.1395  char* _undot_address(const char* address) {
  1.1396      if (!address)
  1.1397          return NULL;
  1.1398 @@ -1773,24 +1657,89 @@
  1.1399      return retval;
  1.1400  }
  1.1401  
  1.1402 -// Unlike pgp_list_keyinfo, this function returns revoked keys.
  1.1403 -static PEP_STATUS _pgp_search_keys(PEP_SESSION session, const char* pattern,
  1.1404 -                                   stringlist_t** keylist, int private_only) {
  1.1405 +static stringpair_list_t *add_key(PEP_SESSION session,
  1.1406 +                                  stringpair_list_t *keyinfo_list,
  1.1407 +                                  stringlist_t* keylist,
  1.1408 +                                  sq_tpk_t tpk, sq_fingerprint_t fpr) {
  1.1409 +    bool revoked = false;
  1.1410 +    // Don't add revoked keys to the keyinfo_list.
  1.1411 +    if (keyinfo_list) {
  1.1412 +        sq_revocation_status_t rs = sq_tpk_revocation_status(tpk);
  1.1413 +        sq_revocation_status_variant_t rsv = sq_revocation_status_variant(rs);
  1.1414 +        sq_revocation_status_free(rs);
  1.1415 +        if (rsv == SQ_REVOCATION_STATUS_REVOKED)
  1.1416 +            revoked = true;
  1.1417 +    }
  1.1418 +
  1.1419 +    if (revoked && ! keylist)
  1.1420 +        return keyinfo_list;
  1.1421 +
  1.1422 +    int dealloc_fpr = 0;
  1.1423 +    if (!fpr) {
  1.1424 +        dealloc_fpr = 1;
  1.1425 +        fpr = sq_tpk_fingerprint(tpk);
  1.1426 +    }
  1.1427 +    char *fpr_str = sq_fingerprint_to_hex(fpr);
  1.1428 +
  1.1429 +    if (!revoked && keyinfo_list) {
  1.1430 +        char *user_id = sq_tpk_primary_user_id(tpk);
  1.1431 +        if (user_id)
  1.1432 +            keyinfo_list = stringpair_list_add(keyinfo_list,
  1.1433 +                                               new_stringpair(fpr_str, user_id));
  1.1434 +        free(user_id);
  1.1435 +    }
  1.1436 +
  1.1437 +    if (keylist)
  1.1438 +        keylist = stringlist_add(keylist, fpr_str);
  1.1439 +
  1.1440 +    free(fpr_str);
  1.1441 +    if (dealloc_fpr)
  1.1442 +        sq_fingerprint_free(fpr);
  1.1443 +
  1.1444 +    return keyinfo_list;
  1.1445 +}
  1.1446 +
  1.1447 +static PEP_STATUS list_keys(PEP_SESSION session,
  1.1448 +                            const char* pattern, int private_only,
  1.1449 +                            stringpair_list_t** keyinfo_list, stringlist_t** keylist)
  1.1450 +{
  1.1451      PEP_STATUS status = PEP_STATUS_OK;
  1.1452 -    sq_binding_t binding = NULL;
  1.1453      sq_tpk_t tpk = NULL;
  1.1454 -    sq_fingerprint_t fingerprint = NULL;
  1.1455 -    char *fingerprint_hex = NULL;
  1.1456 +    sq_fingerprint_t fpr = NULL;
  1.1457  
  1.1458 -    *keylist = NULL;
  1.1459 +    T("('%s', private: %d)", pattern, private_only);
  1.1460  
  1.1461 -    // XXX: We only return an exact match.
  1.1462 -    T("(pattern: %s, private_only: %d)", pattern, private_only);
  1.1463 +    stringpair_list_t* _keyinfo_list = NULL;
  1.1464 +    if (keyinfo_list) {
  1.1465 +        _keyinfo_list = new_stringpair_list(NULL);
  1.1466 +        if (!_keyinfo_list)
  1.1467 +            ERROR_OUT(session, PEP_OUT_OF_MEMORY, "new_stringpair_list");
  1.1468 +    }
  1.1469 +    stringlist_t* _keylist = NULL;
  1.1470 +    if (keylist) {
  1.1471 +        _keylist = new_stringlist(NULL);
  1.1472 +        if (!_keylist)
  1.1473 +            ERROR_OUT(session, PEP_OUT_OF_MEMORY, "new_string_list");
  1.1474 +    }
  1.1475  
  1.1476 -    binding = sq_store_lookup(session->ctx, session->store, pattern);
  1.1477 -    if (! binding) {
  1.1478 -        // No binding is not an error: it means there is no match.
  1.1479 -        if (pattern != NULL) {
  1.1480 +    // Trim any leading space.  This also makes it easier to recognize
  1.1481 +    // a string that is only whitespace.
  1.1482 +    while (*pattern == ' ')
  1.1483 +        pattern ++;
  1.1484 +
  1.1485 +    if (strchr(pattern, '@')) {
  1.1486 +        // Looks like a mailbox.
  1.1487 +        sq_tpk_t *tpks = NULL;
  1.1488 +        int count = 0;
  1.1489 +        status = tpk_find_by_email(session, pattern, private_only, &tpks, &count);
  1.1490 +        ERROR_OUT(session, status, "Looking up '%s'", pattern);
  1.1491 +        for (int i = 0; i < count; i ++) {
  1.1492 +            add_key(session, _keyinfo_list, _keylist, tpks[i], NULL);
  1.1493 +            sq_tpk_free(tpks[i]);
  1.1494 +        }
  1.1495 +        free(tpks);
  1.1496 +
  1.1497 +        if (count == 0) {
  1.1498              // If match failed, check to see if we've got a dotted
  1.1499              // address in the pattern.  If so, try again without dots.
  1.1500              const char* dotpos = strstr(pattern, ".");
  1.1501 @@ -1798,63 +1747,101 @@
  1.1502              if (dotpos && atpos && (dotpos < atpos)) {
  1.1503                  char* undotted = _undot_address(pattern);
  1.1504                  if (undotted) {
  1.1505 -                    PEP_STATUS status = _pgp_search_keys(session, undotted,
  1.1506 -                                                         keylist, private_only);
  1.1507 +                    PEP_STATUS status = list_keys(session, undotted, private_only,
  1.1508 +                                                  keyinfo_list, keylist);
  1.1509                      free(undotted);
  1.1510                      return status;
  1.1511                  }
  1.1512              }
  1.1513          }
  1.1514 -        goto out;
  1.1515 +    } else if (// Only hex characters and spaces
  1.1516 +               pattern[strspn(pattern, "0123456789aAbBcCdDeEfF ")] == 0
  1.1517 +               // And a fair amount of them.
  1.1518 +               && strlen(pattern) >= 16) {
  1.1519 +        // Fingerprint.
  1.1520 +        fpr = sq_fingerprint_from_hex(pattern);
  1.1521 +        status = tpk_find_by_fpr(session, fpr, false, &tpk, NULL);
  1.1522 +        ERROR_OUT(session, status, "Looking up key");
  1.1523 +        add_key(session, _keyinfo_list, _keylist, tpk, fpr);
  1.1524 +    } else if (pattern[0] == 0) {
  1.1525 +        // Empty string.
  1.1526 +
  1.1527 +        sq_tpk_t *tpks = NULL;
  1.1528 +        int count = 0;
  1.1529 +        status = tpk_all(session, private_only, &tpks, &count);
  1.1530 +        ERROR_OUT(session, status, "Looking up '%s'", pattern);
  1.1531 +        for (int i = 0; i < count; i ++) {
  1.1532 +            add_key(session, _keyinfo_list, _keylist, tpks[i], NULL);
  1.1533 +            sq_tpk_free(tpks[i]);
  1.1534 +        }
  1.1535 +        free(tpks);
  1.1536 +    } else {
  1.1537 +        T("unsupported pattern '%s'", pattern);
  1.1538      }
  1.1539  
  1.1540 -    tpk = sq_binding_tpk(session->ctx, binding);
  1.1541 -    if (! tpk)
  1.1542 -        ERROR_OUT(session, PEP_GET_KEY_FAILED, "Getting TPK");
  1.1543 -
  1.1544 -    fingerprint = sq_tpk_fingerprint(tpk);
  1.1545 -    if (!fingerprint)
  1.1546 -        ERROR_OUT(session, PEP_OUT_OF_MEMORY, "Getting fingerprint");
  1.1547 -
  1.1548 -    if (private_only) {
  1.1549 -        // See if we have the private key.
  1.1550 -        status = tsk_find_by_fpr(session, fingerprint, NULL);
  1.1551 -        ERROR_OUT(session, status, "No private key material");
  1.1552 -    }
  1.1553 -
  1.1554 -    fingerprint_hex = sq_fingerprint_to_hex(fingerprint);
  1.1555 -    if (!fingerprint_hex)
  1.1556 -        ERROR_OUT(session, PEP_OUT_OF_MEMORY, "sq_fingerprint_to_hex");
  1.1557 -
  1.1558 -    stringlist_t *_keylist = new_stringlist(fingerprint_hex);
  1.1559 -    if (_keylist == NULL)
  1.1560 -        ERROR_OUT(session, PEP_OUT_OF_MEMORY, "new_stringlist");
  1.1561 -    *keylist = _keylist;
  1.1562 -
  1.1563   out:
  1.1564 -    free(fingerprint_hex);
  1.1565 -    if (fingerprint)
  1.1566 -        sq_fingerprint_free(fingerprint);
  1.1567      if (tpk)
  1.1568          sq_tpk_free(tpk);
  1.1569 -    if (binding)
  1.1570 -        sq_binding_free(binding);
  1.1571 +    if (fpr)
  1.1572 +        sq_fingerprint_free(fpr);
  1.1573  
  1.1574 -    T("(pattern: %s, private_only: %d) -> %s",
  1.1575 -      pattern, private_only, pep_status_to_string(status));
  1.1576 +    if (status == PEP_KEY_NOT_FOUND)
  1.1577 +        status = PEP_STATUS_OK;
  1.1578 +
  1.1579 +    if (status != PEP_STATUS_OK || (_keyinfo_list && !_keyinfo_list->value)) {
  1.1580 +        free_stringpair_list(_keyinfo_list);
  1.1581 +        _keyinfo_list = NULL;
  1.1582 +    }
  1.1583 +    if (keyinfo_list)
  1.1584 +        *keyinfo_list = _keyinfo_list;
  1.1585 +
  1.1586 +    if (status != PEP_STATUS_OK || (_keylist && !_keylist->value)) {
  1.1587 +        free_stringlist(_keylist);
  1.1588 +        _keylist = NULL;
  1.1589 +    }
  1.1590 +    if (keylist)
  1.1591 +        *keylist = _keylist;
  1.1592 +
  1.1593 +    int len = -1;
  1.1594 +    if (keylist)
  1.1595 +        len = stringlist_length(*keylist);
  1.1596 +    else if (keyinfo_list)
  1.1597 +        len = stringpair_list_length(*keyinfo_list);
  1.1598 +    T("(%s) -> %s (%d keys)", pattern, pep_status_to_string(status), len);
  1.1599      return status;
  1.1600  }
  1.1601  
  1.1602 +// pattern could be empty, an fpr, or a mailbox.
  1.1603 +//
  1.1604 +// keyinfo_list is a list of <fpr, openpgp userid> tuples for the
  1.1605 +// matching keys.
  1.1606 +//
  1.1607 +// This function filters out revoked key, but not expired keys.
  1.1608 +PEP_STATUS pgp_list_keyinfo(PEP_SESSION session,
  1.1609 +                            const char* pattern,
  1.1610 +                            stringpair_list_t** keyinfo_list)
  1.1611 +{
  1.1612 +    return list_keys(session, pattern, false, keyinfo_list, NULL);
  1.1613 +}
  1.1614 +
  1.1615 +PEP_STATUS pgp_recv_key(PEP_SESSION session, const char *pattern)
  1.1616 +{
  1.1617 +    assert(!"pgp_recv_key not implemented");
  1.1618 +    return PEP_UNKNOWN_ERROR;
  1.1619 +}
  1.1620 +
  1.1621 +// Unlike pgp_list_keyinfo, this function returns revoked keys.
  1.1622  PEP_STATUS pgp_find_keys(
  1.1623      PEP_SESSION session, const char *pattern, stringlist_t **keylist)
  1.1624  {
  1.1625 -    return _pgp_search_keys(session, pattern, keylist, 0);
  1.1626 +    return list_keys(session, pattern, false, NULL, keylist);
  1.1627  }
  1.1628  
  1.1629 +// Unlike pgp_list_keyinfo, this function returns revoked keys.
  1.1630  PEP_STATUS pgp_find_private_keys(
  1.1631      PEP_SESSION session, const char *pattern, stringlist_t **keylist)
  1.1632  {
  1.1633 -    return _pgp_search_keys(session, pattern, keylist, 1);
  1.1634 +    return list_keys(session, pattern, true, NULL, keylist);
  1.1635  }
  1.1636  
  1.1637  PEP_STATUS pgp_send_key(PEP_SESSION session, const char *pattern)
  1.1638 @@ -1876,7 +1863,7 @@
  1.1639      *comm_type = PEP_ct_unknown;
  1.1640  
  1.1641      sq_fingerprint_t sq_fpr = sq_fingerprint_from_hex(fpr);
  1.1642 -    status = tpk_find_by_fpr(session, sq_fpr, &tpk);
  1.1643 +    status = tpk_find_by_fpr(session, sq_fpr, false, &tpk, NULL);
  1.1644      sq_fingerprint_free(sq_fpr);
  1.1645      ERROR_OUT(session, status, "Looking up key: %s", fpr);
  1.1646  
  1.1647 @@ -1959,16 +1946,14 @@
  1.1648      PEP_SESSION session, const char *fpr, const timestamp *ts)
  1.1649  {
  1.1650      PEP_STATUS status = PEP_STATUS_OK;
  1.1651 -    sq_tsk_t tsk = NULL;
  1.1652      sq_tpk_t tpk = NULL;
  1.1653 -
  1.1654      time_t t = mktime((struct tm *) ts);
  1.1655  
  1.1656 -    status = tsk_find_by_fpr_hex(session, fpr, &tsk);
  1.1657 +    T("(%s)", fpr);
  1.1658 +
  1.1659 +    status = tpk_find_by_fpr_hex(session, fpr, true, &tpk, NULL);
  1.1660      ERROR_OUT(session, status, "Looking up '%s'", fpr);
  1.1661  
  1.1662 -    tpk = sq_tsk_into_tpk(tsk);
  1.1663 -
  1.1664      uint32_t creation_time = sq_p_key_creation_time(sq_tpk_primary(tpk));
  1.1665      if (creation_time > t)
  1.1666          // The creation time is after the expiration time!
  1.1667 @@ -1980,7 +1965,7 @@
  1.1668      if (! tpk)
  1.1669          ERROR_OUT(session, PEP_UNKNOWN_ERROR, "setting expiration");
  1.1670  
  1.1671 -    status = tpk_save(session, tpk, NULL, NULL, false);
  1.1672 +    status = tpk_save(session, tpk, NULL);
  1.1673      tpk = NULL;
  1.1674      ERROR_OUT(session, status, "Saving %s", fpr);
  1.1675  
  1.1676 @@ -1988,6 +1973,7 @@
  1.1677      if (tpk)
  1.1678          sq_tpk_free(tpk);
  1.1679  
  1.1680 +    T("(%s) -> %s", fpr, pep_status_to_string(status));
  1.1681      return status;
  1.1682  }
  1.1683  
  1.1684 @@ -1995,13 +1981,13 @@
  1.1685      PEP_SESSION session, const char *fpr, const char *reason)
  1.1686  {
  1.1687      PEP_STATUS status = PEP_STATUS_OK;
  1.1688 -    sq_tsk_t tsk = NULL;
  1.1689      sq_tpk_t tpk = NULL;
  1.1690  
  1.1691 -    status = tsk_find_by_fpr_hex(session, fpr, &tsk);
  1.1692 +    T("(%s)", fpr);
  1.1693 +
  1.1694 +    status = tpk_find_by_fpr_hex(session, fpr, true, &tpk, NULL);
  1.1695      ERROR_OUT(session, status, "Looking up %s", fpr);
  1.1696  
  1.1697 -    tpk = sq_tsk_into_tpk(tsk);
  1.1698      tpk = sq_tpk_revoke_in_place(session->ctx, tpk,
  1.1699                                   SQ_REASON_FOR_REVOCATION_UNSPECIFIED,
  1.1700                                   reason);
  1.1701 @@ -2011,7 +1997,7 @@
  1.1702      assert(sq_revocation_status_variant(sq_tpk_revocation_status(tpk))
  1.1703             == SQ_REVOCATION_STATUS_REVOKED);
  1.1704  
  1.1705 -    status = tpk_save(session, tpk, NULL, NULL, false);
  1.1706 +    status = tpk_save(session, tpk, NULL);
  1.1707      tpk = NULL;
  1.1708      ERROR_OUT(session, status, "Saving %s", fpr);
  1.1709  
  1.1710 @@ -2019,6 +2005,7 @@
  1.1711      if (tpk)
  1.1712          sq_tpk_free(tpk);
  1.1713  
  1.1714 +    T("(%s) -> %s", fpr, pep_status_to_string(status));
  1.1715      return status;
  1.1716  }
  1.1717  
  1.1718 @@ -2027,6 +2014,7 @@
  1.1719  {
  1.1720      PEP_STATUS status = PEP_STATUS_OK;
  1.1721      sq_tpk_t tpk = NULL;
  1.1722 +    T("(%s)", fpr);
  1.1723  
  1.1724      assert(session);
  1.1725      assert(fpr);
  1.1726 @@ -2035,7 +2023,7 @@
  1.1727      *expired = false;
  1.1728  
  1.1729      sq_fingerprint_t sq_fpr = sq_fingerprint_from_hex(fpr);
  1.1730 -    status = tpk_find_by_fpr(session, sq_fpr, &tpk);
  1.1731 +    status = tpk_find_by_fpr(session, sq_fpr, false, &tpk, NULL);
  1.1732      sq_fingerprint_free(sq_fpr);
  1.1733      ERROR_OUT(session, status, "Looking up %s", fpr);
  1.1734  
  1.1735 @@ -2080,6 +2068,7 @@
  1.1736   out:
  1.1737      if (tpk)
  1.1738          sq_tpk_free(tpk);
  1.1739 +    T("(%s) -> %s", fpr, pep_status_to_string(status));
  1.1740      return status;
  1.1741  }
  1.1742  
  1.1743 @@ -2088,6 +2077,8 @@
  1.1744      PEP_STATUS status = PEP_STATUS_OK;
  1.1745      sq_tpk_t tpk;
  1.1746  
  1.1747 +    T("(%s)", fpr);
  1.1748 +
  1.1749      assert(session);
  1.1750      assert(fpr);
  1.1751      assert(revoked);
  1.1752 @@ -2095,7 +2086,7 @@
  1.1753      *revoked = false;
  1.1754  
  1.1755      sq_fingerprint_t sq_fpr = sq_fingerprint_from_hex(fpr);
  1.1756 -    status = tpk_find_by_fpr(session, sq_fpr, &tpk);
  1.1757 +    status = tpk_find_by_fpr(session, sq_fpr, false, &tpk, NULL);
  1.1758      sq_fingerprint_free(sq_fpr);
  1.1759      ERROR_OUT(session, status, "Looking up %s", fpr);
  1.1760  
  1.1761 @@ -2105,6 +2096,7 @@
  1.1762      sq_tpk_free(tpk);
  1.1763  
  1.1764   out:
  1.1765 +    T("(%s) -> %s", fpr, pep_status_to_string(status));
  1.1766      return status;
  1.1767  }
  1.1768  
  1.1769 @@ -2112,11 +2104,12 @@
  1.1770  {
  1.1771      PEP_STATUS status = PEP_STATUS_OK;
  1.1772      sq_tpk_t tpk = NULL;
  1.1773 +    T("(%s)", fpr);
  1.1774  
  1.1775      *created = 0;
  1.1776  
  1.1777      sq_fingerprint_t sq_fpr = sq_fingerprint_from_hex(fpr);
  1.1778 -    status = tpk_find_by_fpr(session, sq_fpr, &tpk);
  1.1779 +    status = tpk_find_by_fpr(session, sq_fpr, false, &tpk, NULL);
  1.1780      sq_fingerprint_free(sq_fpr);
  1.1781      ERROR_OUT(session, status, "Looking up %s", fpr);
  1.1782  
  1.1783 @@ -2125,6 +2118,7 @@
  1.1784      sq_tpk_free(tpk);
  1.1785  
  1.1786   out:
  1.1787 +    T("(%s) -> %s", fpr, pep_status_to_string(status));
  1.1788      return status;
  1.1789  }
  1.1790  
  1.1791 @@ -2136,16 +2130,16 @@
  1.1792  PEP_STATUS pgp_contains_priv_key(PEP_SESSION session, const char *fpr,
  1.1793                                   bool *has_private)
  1.1794  {
  1.1795 +    T("(%s)", fpr);
  1.1796      sq_fingerprint_t sq_fpr = sq_fingerprint_from_hex(fpr);
  1.1797 -    PEP_STATUS status = tsk_find_by_fpr(session, sq_fpr, NULL);
  1.1798 +    PEP_STATUS status = tpk_find_by_fpr(session, sq_fpr, true, NULL, NULL);
  1.1799      sq_fingerprint_free(sq_fpr);
  1.1800      if (status == PEP_STATUS_OK) {
  1.1801          *has_private = 1;
  1.1802 -        return PEP_STATUS_OK;
  1.1803      } else if (status == PEP_KEY_NOT_FOUND) {
  1.1804          *has_private = 0;
  1.1805 -        return PEP_STATUS_OK;
  1.1806 -    } else {
  1.1807 -        return status;
  1.1808 +        status = PEP_STATUS_OK;
  1.1809      }
  1.1810 +    T("(%s) -> %s", fpr, pep_status_to_string(status));
  1.1811 +    return status;
  1.1812  }