All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.named_data.jndn.security.pib.AndroidSqlite3Pib Maven / Gradle / Ivy

/**
 * Copyright (C) 2017-2019 Regents of the University of California.
 * @author: Jeff Thompson 
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 * A copy of the GNU Lesser General Public License is in the file COPYING.
 */

package net.named_data.jndn.security.pib;

import android.database.sqlite.SQLiteDatabase;
import android.database.Cursor;
import android.content.ContentValues;
import android.database.sqlite.SQLiteStatement;
import android.database.sqlite.SQLiteDoneException;
import java.io.File;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashSet;
import net.named_data.jndn.Name;
import net.named_data.jndn.encoding.EncodingException;
import net.named_data.jndn.security.v2.CertificateV2;
import net.named_data.jndn.util.Blob;

/**
 * AndroidSqlite3Pib extends PibImpl and is used by the Pib class as an
 * implementation of a PIB using the android.database.sqlite API. All the
 * contents in the PIB are stored in an SQLite3 database file. This provides
 * more persistent storage than PibMemory.
 */
public class AndroidSqlite3Pib extends PibSqlite3Base {
  /**
   * Create a new AndroidSqlite3Pib to work with an SQLite3 file. This assumes
   * that the database directory does not contain a PIB database of an older
   * version.
   * @param databaseDirectoryPath The directory where the database file is
   * located. If the directory does not exist, create it. An Android app can use
   * context.getFilesDir() .
   * @param databaseFilename The name if the database file in the
   * databaseDirectoryPath.
   * @throws PibImpl.Error if initialization fails.
   */
  public AndroidSqlite3Pib
    (String databaseDirectoryPath, String databaseFilename) throws PibImpl.Error
  {
    construct(databaseDirectoryPath, databaseFilename);
  }

  /**
   * Create a new AndroidSqlite3Pib to work with an SQLite3 file. This assumes
   * that the database directory does not contain a PIB database of an older
   * version. Use "pib.db" for the databaseFilename in the databaseDirectoryPath.
   * @param databaseDirectoryPath The directory where the database file is
   * located. If the directory does not exist, create it. An Android app can use
   * context.getFilesDir() .
   * @throws PibImpl.Error if initialization fails.
   */
  public AndroidSqlite3Pib(String databaseDirectoryPath) throws PibImpl.Error
  {
    construct(databaseDirectoryPath, "pib.db");
  }

  private void
  construct(String databaseDirectoryPath, String databaseFilename)
    throws PibImpl.Error
  {
    new File(databaseDirectoryPath).mkdirs();

    File databaseFilePath = new File(databaseDirectoryPath, databaseFilename);
    database_ = SQLiteDatabase.openDatabase
      (databaseFilePath.getAbsolutePath(), null,
       SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.CREATE_IF_NECESSARY);

    // Initialize the PIB tables.
    database_.execSQL(INITIALIZATION1);
    database_.execSQL(INITIALIZATION2);
    database_.execSQL(INITIALIZATION3);
    database_.execSQL(INITIALIZATION4);
    database_.execSQL(INITIALIZATION5);
    database_.execSQL(INITIALIZATION6);
    database_.execSQL(INITIALIZATION7);
  }

  public static String
  getScheme() { return "pib-sqlite3"; }

  // TpmLocator management.

  /**
   * Set the corresponding TPM information to tpmLocator. This method does not
   * reset the contents of the PIB.
   * @param tpmLocator The TPM locator string.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public void
  setTpmLocator(String tpmLocator) throws PibImpl.Error
  {
    if (getTpmLocator().equals("")) {
      // The tpmLocator does not exist. Insert it directly.
      ContentValues values = new ContentValues();
      values.put("tpm_locator", tpmLocator);
      if (database_.insert("tpmInfo", null, values) < 0)
        throw new PibImpl.Error("AndroidSqlite3Pib: SQLite error");
    }
    else {
      // Update the existing tpmLocator.
      ContentValues values = new ContentValues();
      values.put("tpm_locator", tpmLocator);
      database_.update("tpmInfo", values, null, null);
    }
  }

  /**
   * Get the TPM Locator.
   * @return The TPM locator string.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public String
  getTpmLocator() throws PibImpl.Error
  {
    Cursor cursor = database_.rawQuery(SELECT_getTpmLocator, null);

    try {
      if (cursor.moveToNext())
        return cursor.getString(0);
      else
        return "";
    } finally {
      cursor.close();
    }
  }

  // Identity management.

  /**
   * Check for the existence of an identity.
   * @param identityName The name of the identity.
   * @return True if the identity exists, otherwise false.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public boolean
  hasIdentity(Name identityName) throws PibImpl.Error
  {
    // Use a statement because it allows binding a blob for the query.
    SQLiteStatement statement = database_.compileStatement(SELECT_hasIdentity);
    try {
      statement.bindBlob(1, identityName.wireEncode().getImmutableArray());
      try {
        statement.simpleQueryForLong();
        return true;
      } catch (SQLiteDoneException ex) {
        // No match.
        return false;
      }
    } finally {
      statement.close();
    }
  }

  /**
   * Add the identity. If the identity already exists, do nothing. If no default
   * identity has been set, set the added identity as the default.
   * @param identityName The name of the identity to add. This copies the name.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public void
  addIdentity(Name identityName) throws PibImpl.Error
  {
    if (!hasIdentity(identityName)) {
      ContentValues values = new ContentValues();
      values.put("identity", identityName.wireEncode().getImmutableArray());
      if (database_.insert("identities", null, values) < 0)
        throw new PibImpl.Error("AndroidSqlite3Pib: SQLite error");
    }

    if (!hasDefaultIdentity())
      setDefaultIdentity(identityName);
  }

  /**
   * Remove the identity and its related keys and certificates. If the default
   * identity is being removed, no default identity will be selected.  If the
   * identity does not exist, do nothing.
   * @param identityName The name of the identity to remove.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public void
  removeIdentity(Name identityName) throws PibImpl.Error
  {
    byte[] identityBytes = identityName.wireEncode().getImmutableArray();

    // We don't use triggers, so manually delete from keys and certificates.
    // First get the key ids.
    ArrayList keyIds = new ArrayList();

    // Use a hex literal instead of the ending '?' since rawQuery doesn't allow
    // binding a blob.
    Cursor cursor = database_.rawQuery
      (SELECT_removeIdentity.substring(0, SELECT_removeIdentity.length() - 1) +
       "x'" + identityName.wireEncode().toHex() + "'", null);

    try {
      while (cursor.moveToNext())
        keyIds.add(cursor.getInt(0));
    } finally {
      cursor.close();
    }

    for (int keyId : keyIds)
      database_.execSQL
        (DELETE_removeIdentity_certificates, new Object[] { keyId });

    for (int keyId : keyIds)
      database_.execSQL
        (DELETE_removeIdentity_keys, new Object[] { keyId });

    // Now, delete from identities.
    database_.execSQL
      (DELETE_removeIdentity_identity, new Object[] { identityBytes });
  }

  /**
   * Erase all certificates, keys, and identities.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public void
  clearIdentities() throws PibImpl.Error
  {
    // We don't use triggers, so manually delete from keys and certificates.
    database_.execSQL(DELETE_clearIdentities_certificates, new Object[0]);
    database_.execSQL(DELETE_clearIdentities_keys, new Object[0]);

    // Now, delete from identities.
    database_.execSQL(DELETE_clearIdentities_identities, new Object[0]);
  }

  /**
   * Get the names of all the identities.
   * @return The set of identity names. The Name objects are fresh copies.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public HashSet
  getIdentities() throws PibImpl.Error
  {
    HashSet identityNames = new HashSet();

    Cursor cursor = database_.rawQuery(SELECT_getIdentities, null);

    try {
      while (cursor.moveToNext()) {
        Name name = new Name();
        try {
          name.wireDecode(new Blob(cursor.getBlob(0)));
        } catch (EncodingException ex) {
          throw new PibImpl.Error("PibSqlite3: Error decoding name: " + ex);
        }
        identityNames.add(name);
      }
    } finally {
      cursor.close();
    }

    return identityNames;
  }

  /**
   * Set the identity with the identityName as the default identity. If the
   * identity with identityName does not exist, then it will be created.
   * @param identityName The name for the default identity. This copies the name.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public void
  setDefaultIdentity(Name identityName) throws PibImpl.Error
  {
    byte[] identityBytes = identityName.wireEncode().getImmutableArray();

    if (!hasIdentity(identityName)) {
      ContentValues values = new ContentValues();
      values.put("identity", identityBytes);
      if (database_.insert("identities", null, values) < 0)
        throw new PibImpl.Error("AndroidSqlite3Pib: SQLite error");
    }

    // We don't use a trigger, so manually reset the previous default identity.
    database_.execSQL(UPDATE_setDefaultIdentity_reset, new Object[0]);

    // Now set the current default identity.
    // Use a statement because it allows binding a blob for the where clause.
    SQLiteStatement statement = database_.compileStatement
      (UPDATE_setDefaultIdentity_set);
    try {
      statement.bindBlob(1, identityBytes);
      if (statement.executeUpdateDelete() <= 0)
        throw new PibImpl.Error("AndroidSqlite3Pib: SQLite error");
    } finally {
      statement.close();
    }
  }

  /**
   * Get the default identity.
   * @return The name of the default identity, as a fresh copy.
   * @throws Pib.Error for no default identity.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public Name
  getDefaultIdentity() throws Pib.Error, PibImpl.Error
  {
    Cursor cursor = database_.rawQuery(SELECT_getDefaultIdentity, null);

    Name name = new Name();
    try {
      if (cursor.moveToNext()) {
        try {
          name.wireDecode(new Blob(cursor.getBlob(0)));
        } catch (EncodingException ex) {
          throw new PibImpl.Error("PibSqlite3: Error decoding name: " + ex);
        }
      }
      else
        throw new Pib.Error("No default identity");
    } finally {
      cursor.close();
    }

    return name;
  }

  // Key management.

  /**
   * Check for the existence of a key with keyName.
   * @param keyName The name of the key.
   * @return True if the key exists, otherwise false. Return false if the
   * identity does not exist.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public boolean
  hasKey(Name keyName) throws PibImpl.Error
  {
    // Use a statement because it allows binding a blob for the query.
    SQLiteStatement statement = database_.compileStatement(SELECT_hasKey);
    try {
      statement.bindBlob(1, keyName.wireEncode().getImmutableArray());
      try {
        statement.simpleQueryForLong();
        return true;
      } catch (SQLiteDoneException ex) {
        // No match.
        return false;
      }
    } finally {
      statement.close();
    }
  }

  /**
   * Add the key. If a key with the same name already exists, overwrite the key.
   * If the identity does not exist, it will be created. If no default key for
   * the identity has been set, then set the added key as the default for the
   * identity.  If no default identity has been set, identity becomes the
   * default.
   * @param identityName The name of the identity that the key belongs to. This
   * copies the name.
   * @param keyName The name of the key. This copies the name.
   * @param key The public key bits. This copies the array.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public void
  addKey(Name identityName, Name keyName, ByteBuffer key) throws PibImpl.Error
  {
    // Ensure the identity exists.
    addIdentity(identityName);

    if (!hasKey(keyName)) {
      // Use a statement because it allows binding a blob for the where clause.
      SQLiteStatement statement = database_.compileStatement(INSERT_addKey);
      try {
        statement.bindBlob(1, identityName.wireEncode().getImmutableArray());
        statement.bindBlob(2, keyName.wireEncode().getImmutableArray());
        statement.bindBlob(3, new Blob(key, false).getImmutableArray());
        if (statement.executeUpdateDelete() <= 0)
          throw new PibImpl.Error("AndroidSqlite3Pib: SQLite error");
      } finally {
        statement.close();
      }
    }
    else {
      // Use a statement because it allows binding a blob for the where clause.
      SQLiteStatement statement = database_.compileStatement(UPDATE_addKey);
      try {
        statement.bindBlob(1, new Blob(key, false).getImmutableArray());
        statement.bindBlob(2, keyName.wireEncode().getImmutableArray());
        if (statement.executeUpdateDelete() <= 0)
          throw new PibImpl.Error("AndroidSqlite3Pib: SQLite error");
      } finally {
        statement.close();
      }
    }

    if (!hasDefaultKeyOfIdentity(identityName)) {
      try {
        setDefaultKeyOfIdentity(identityName, keyName);
      } catch (Pib.Error ex) {
        throw new PibImpl.Error("PibSqlite3: Error setting the default key: " + ex);
      }
    }
  }

  /**
   * Remove the key with keyName and its related certificates. If the key does
   * not exist, do nothing.
   * @param keyName The name of the key.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public void
  removeKey(Name keyName) throws PibImpl.Error
  {
    byte[] keyNameBytes = keyName.wireEncode().getImmutableArray();

    // We don't use triggers, so manually delete from certificates.
    database_.execSQL
      (DELETE_removeKey_certificates, new Object[] { keyNameBytes });

    // Now, delete from keys.
    database_.execSQL
      (DELETE_removeKey_keys, new Object[] { keyNameBytes });
  }

  /**
   * Get the key bits of a key with name keyName.
   * @param keyName The name of the key.
   * @return The key bits.
   * @throws Pib.Error if the key does not exist.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public Blob
  getKeyBits(Name keyName) throws Pib.Error, PibImpl.Error
  {
    // First use a statement to get the key ID because the statement allows
    // binding a blob for the query.
    long keyId;
    SQLiteStatement statement = database_.compileStatement
      ("SELECT keys.id " + FROM_WHERE_getKeyBits);
    try {
      statement.bindBlob(1, keyName.wireEncode().getImmutableArray());
      try {
        keyId = statement.simpleQueryForLong();
      } catch (SQLiteDoneException ex) {
        throw new Pib.Error("Key `" + keyName.toUri() + "` does not exist");
      }
    } finally {
      statement.close();
    }

    // Now use the keyId to get the key.
    Cursor cursor = database_.rawQuery("SELECT key_bits FROM keys WHERE id=?",
       new String[] { Long.toString(keyId) });
    try {
      if (cursor.moveToNext())
        return new Blob(cursor.getBlob(0));
      else
        // We don't expect this since we got the keyId.
        throw new Pib.Error("Key `" + keyName.toUri() + "` does not exist");
    } finally {
      cursor.close();
    }
  }

  /**
   * Get all the key names of the identity with the name identityName. The
   * returned key names can be used to create a KeyContainer. With a key name
   * and a backend implementation, one can create a Key front end instance.
   * @param identityName The name of the identity.
   * @return The set of key names. The Name objects are fresh copies. If the
   * identity does not exist, return an empty set.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public HashSet
  getKeysOfIdentity(Name identityName) throws PibImpl.Error
  {
    HashSet keyNames = new HashSet();

    // Use a hex literal instead of the ending '?' since rawQuery doesn't allow
    // binding a blob.
    Cursor cursor = database_.rawQuery
      (SELECT_getKeysOfIdentity.substring(0, SELECT_getKeysOfIdentity.length() - 1) +
       "x'" + identityName.wireEncode().toHex() + "'", null);

    try {
      while (cursor.moveToNext()) {
        Name name = new Name();
        try {
          name.wireDecode(new Blob(cursor.getBlob(0)));
        } catch (EncodingException ex) {
          throw new PibImpl.Error("PibSqlite3: Error decoding name: " + ex);
        }
        keyNames.add(name);
      }
    } finally {
      cursor.close();
    }

    return keyNames;
  }

  /**
   * Set the key with keyName as the default key for the identity with name
   * identityName.
   * @param identityName The name of the identity. This copies the name.
   * @param keyName The name of the key. This copies the name.
   * @throws Pib.Error if the key does not exist.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public void
  setDefaultKeyOfIdentity(Name identityName, Name keyName)
    throws Pib.Error, PibImpl.Error
  {
    if (!hasKey(keyName))
      throw new Pib.Error("Key `" + keyName.toUri() + "` does not exist");

    // We don't use a trigger, so manually reset the previous default key.
    database_.execSQL(UPDATE_setDefaultKeyOfIdentity_reset, new Object[0]);

    // Now set the current default key.
    // Use a statement because it allows binding a blob for the where clause.
    SQLiteStatement statement = database_.compileStatement
      (UPDATE_setDefaultKeyOfIdentity_set);
    try {
      statement.bindBlob(1, keyName.wireEncode().getImmutableArray());
      if (statement.executeUpdateDelete() <= 0)
        throw new PibImpl.Error("AndroidSqlite3Pib: SQLite error");
    } finally {
      statement.close();
    }
  }

  /**
   * Get the name of the default key for the identity with name identityName.
   * @param identityName The name of the identity.
   * @return The name of the default key, as a fresh copy.
   * @throws Pib.Error if there is no default key or if the identity does not
   * exist.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public Name
  getDefaultKeyOfIdentity(Name identityName) throws Pib.Error, PibImpl.Error
  {
    if (!hasIdentity(identityName))
      throw new Pib.Error
        ("Identity `" + identityName.toUri() + "` does not exist");

    // First use a statement to get the key ID because the statement allows
    // binding a blob for the query.
    long keyId;
    SQLiteStatement statement = database_.compileStatement
      ("SELECT keys.id " + FROM_WHERE_getDefaultKeyOfIdentity);
    try {
      statement.bindBlob(1, identityName.wireEncode().getImmutableArray());
      try {
        keyId = statement.simpleQueryForLong();
      } catch (SQLiteDoneException ex) {
        throw new Pib.Error
          ("No default key for identity `" + identityName.toUri() + "`");
      }
    } finally {
      statement.close();
    }

    // Now use the keyId to get the key name.
    Cursor cursor = database_.rawQuery("SELECT key_name FROM keys WHERE id=?",
       new String[] { Long.toString(keyId) });
    try {
      if (cursor.moveToNext()) {
        Name name = new Name();
        try {
          name.wireDecode(new Blob(cursor.getBlob(0)));
        } catch (EncodingException ex) {
          throw new PibImpl.Error("PibSqlite3: Error decoding name: " + ex);
        }
        return name;
      }
      else
        // We don't expect this since we got the keyId.
        throw new Pib.Error
          ("No default key for identity `" + identityName.toUri() + "`");
    } finally {
      cursor.close();
    }
  }

  // Certificate management.

  /**
   * Check for the existence of a certificate with name certificateName.
   * @param certificateName The name of the certificate.
   * @return True if the certificate exists, otherwise false.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public boolean
  hasCertificate(Name certificateName) throws PibImpl.Error
  {
    // Use a statement because it allows binding a blob for the query.
    SQLiteStatement statement = database_.compileStatement(SELECT_hasCertificate);
    try {
      statement.bindBlob(1, certificateName.wireEncode().getImmutableArray());
      try {
        statement.simpleQueryForLong();
        return true;
      } catch (SQLiteDoneException ex) {
        // No match.
        return false;
      }
    } finally {
      statement.close();
    }
  }

  /**
   * Add the certificate. If a certificate with the same name (without implicit
   * digest) already exists, then overwrite the certificate. If the key or
   * identity does not exist, they will be created. If no default certificate
   * for the key has been set, then set the added certificate as the default for
   * the key. If no default key was set for the identity, it will be set as the
   * default key for the identity. If no default identity was selected, the
   * certificate's identity becomes the default.
   * @param certificate The certificate to add. This copies the object.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public void
  addCertificate(CertificateV2 certificate) throws PibImpl.Error
  {
    // Ensure the key exists.
    Blob content = certificate.getContent();
    addKey(certificate.getIdentity(), certificate.getKeyName(), content.buf());

    if (!hasCertificate(certificate.getName())) {
      // Use a statement because it allows binding a blob for the where clause.
      SQLiteStatement statement = database_.compileStatement
        (INSERT_addCertificate);
      try {
        statement.bindBlob(1, certificate.getKeyName().wireEncode().getImmutableArray());
        statement.bindBlob(2, certificate.getName().wireEncode().getImmutableArray());
        statement.bindBlob(3, certificate.wireEncode().getImmutableArray());
        if (statement.executeUpdateDelete() <= 0)
          throw new PibImpl.Error("AndroidSqlite3Pib: SQLite error");
      } finally {
        statement.close();
      }
    }
    else {
      // Use a statement because it allows binding a blob for the where clause.
      SQLiteStatement statement = database_.compileStatement
        (UPDATE_addCertificate);
      try {
        statement.bindBlob(1, certificate.wireEncode().getImmutableArray());
        statement.bindBlob(2, certificate.getName().wireEncode().getImmutableArray());
        if (statement.executeUpdateDelete() <= 0)
          throw new PibImpl.Error("AndroidSqlite3Pib: SQLite error");
      } finally {
        statement.close();
      }
    }

    if (!hasDefaultCertificateOfKey(certificate.getKeyName())) {
      try {
        setDefaultCertificateOfKey(certificate.getKeyName(), certificate.getName());
      } catch (Pib.Error ex) {
        throw new PibImpl.Error
          ("PibSqlite3: Error setting the default certificate: " + ex);
      }
    }
  }

  /**
   * Remove the certificate with name certificateName. If the certificate does
   * not exist, do nothing.
   * @param certificateName The name of the certificate.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public void
  removeCertificate(Name certificateName) throws PibImpl.Error
  {
    database_.execSQL
      (DELETE_removeCertificate,
       new Object[] { certificateName.wireEncode().getImmutableArray() });
  }

  /**
   * Get the certificate with name certificateName.
   * @param certificateName The name of the certificate.
   * @return A copy of the certificate.
   * @throws Pib.Error if the certificate does not exist.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public CertificateV2
  getCertificate(Name certificateName) throws Pib.Error, PibImpl.Error
  {
    // First use a statement to get the certificate ID because the statement
    // allows binding a blob for the query.
    long certificateId;
    SQLiteStatement statement = database_.compileStatement
      ("SELECT certificates.id FROM certificates WHERE certificate_name=?");
    try {
      statement.bindBlob(1,certificateName.wireEncode().getImmutableArray());
      try {
        certificateId = statement.simpleQueryForLong();
      } catch (SQLiteDoneException ex) {
        throw new Pib.Error
          ("Certificate `" + certificateName.toUri() + "` does not exit");
      }
    } finally {
      statement.close();
    }

    // Now use the certificateId to get the certicicate.
    Cursor cursor = database_.rawQuery
      ("SELECT certificate_data FROM certificates WHERE id=?",
       new String[] { Long.toString(certificateId) });
    try {
      if (cursor.moveToNext()) {
        CertificateV2 certificate = new CertificateV2();
        try {
          certificate.wireDecode(new Blob(cursor.getBlob(0)));
        } catch (EncodingException ex) {
          throw new PibImpl.Error("PibSqlite3: Error decoding certificate: " + ex);
        }
        return certificate;
      }
      else
        // We don't expect this since we got the certificateId.
        throw new Pib.Error
          ("Certificate `" + certificateName.toUri() + "` does not exit");
    } finally {
      cursor.close();
    }
  }

  /**
   * Get a list of certificate names of the key with id keyName. The returned
   * certificate names can be used to create a PibCertificateContainer. With a
   * certificate name and a backend implementation, one can obtain the
   * certificate.
   * @param keyName The name of the key.
   * @return The set of certificate names. The Name objects are fresh copies. If
   * the key does not exist, return an empty set.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public HashSet
  getCertificatesOfKey(Name keyName) throws PibImpl.Error
  {
    HashSet certNames = new HashSet();

    // Use a hex literal instead of the ending '?' since rawQuery doesn't allow
    // binding a blob.
    Cursor cursor = database_.rawQuery
      (SELECT_getCertificatesOfKey.substring(0, SELECT_getCertificatesOfKey.length() - 1) +
       "x'" + keyName.wireEncode().toHex() + "'", null);

    try {
      while (cursor.moveToNext()) {
        Name name = new Name();
        try {
          name.wireDecode(new Blob(cursor.getBlob(0)));
        } catch (EncodingException ex) {
          throw new PibImpl.Error("PibSqlite3: Error decoding name: " + ex);
        }
        certNames.add(name);
      }
    } finally {
      cursor.close();
    }

    return certNames;
  }

  /**
   * Set the cert with name certificateName as the default for the key with
   * keyName.
   * @param keyName The name of the key.
   * @param certificateName The name of the certificate. This copies the name.
   * @throws Pib.Error if the certificate with name certificateName does not
   * exist.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public void
  setDefaultCertificateOfKey(Name keyName, Name certificateName)
    throws Pib.Error, PibImpl.Error
  {
    if (!hasCertificate(certificateName))
      throw new Pib.Error
        ("Certificate `" + certificateName.toUri() + "` does not exist");

    // We don't use a trigger, so manually reset the previous default certificate.
    database_.execSQL(UPDATE_setDefaultCertificateOfKey_reset, new Object[0]);

    // Now set the current default certificate.
    // Use a statement because it allows binding a blob for the where clause.
    SQLiteStatement statement = database_.compileStatement
      (UPDATE_setDefaultCertificateOfKey_set);
    try {
      statement.bindBlob(1, certificateName.wireEncode().getImmutableArray());
      if (statement.executeUpdateDelete() <= 0)
        throw new PibImpl.Error("AndroidSqlite3Pib: SQLite error");
    } finally {
      statement.close();
    }
  }

  /**
   * Get the default certificate for the key with eyName.
   * @param keyName The name of the key.
   * @return A copy of the default certificate.
   * @throws Pib.Error if the default certificate does not exist.
   * @throws PibImpl.Error for a non-semantic (database access) error.
   */
  public CertificateV2
  getDefaultCertificateOfKey(Name keyName) throws Pib.Error, PibImpl.Error
  {
    // First use a statement to get the certificate ID because the statement allows
    // binding a blob for the query.
    long certificateId;
    SQLiteStatement statement = database_.compileStatement
      ("SELECT certificates.id " + FROM_WHERE_getDefaultCertificateOfKey);
    try {
      statement.bindBlob(1, keyName.wireEncode().getImmutableArray());
      try {
        certificateId = statement.simpleQueryForLong();
      } catch (SQLiteDoneException ex) {
        throw new Pib.Error
          ("No default certificate for key `" + keyName.toUri() + "`");
      }
    } finally {
      statement.close();
    }

    // Now use the certificateId to get the key name.
    Cursor cursor = database_.rawQuery
      ("SELECT certificate_data FROM certificates WHERE id=?",
       new String[] { Long.toString(certificateId) });
    try {
      if (cursor.moveToNext()) {
        CertificateV2 certificate = new CertificateV2();
        try {
          certificate.wireDecode(new Blob(cursor.getBlob(0)));
        } catch (EncodingException ex) {
          throw new PibImpl.Error("PibSqlite3: Error decoding certificate: " + ex);
        }
        return certificate;
      }
      else
        // We don't expect this since we got the certificateId.
        throw new Pib.Error
          ("No default certificate for key `" + keyName.toUri() + "`");
    } finally {
      cursor.close();
    }
  }

  private boolean
  hasDefaultIdentity() throws PibImpl.Error
  {
    // Use a statement because it allows binding a blob for the query.
    SQLiteStatement statement = database_.compileStatement
      (SELECT_hasDefaultIdentity);
    try {
      try {
        statement.simpleQueryForLong();
        return true;
      } catch (SQLiteDoneException ex) {
        // No match.
        return false;
      }
    } finally {
      statement.close();
    }
  }

  private boolean
  hasDefaultKeyOfIdentity(Name identityName) throws PibImpl.Error
  {
    // Use a statement because it allows binding a blob for the query.
    SQLiteStatement statement = database_.compileStatement
      (SELECT_hasDefaultKeyOfIdentity);
    try {
      statement.bindBlob(1, identityName.wireEncode().getImmutableArray());
      try {
        statement.simpleQueryForLong();
        return true;
      } catch (SQLiteDoneException ex) {
        // No match.
        return false;
      }
    } finally {
      statement.close();
    }
  }

  private boolean
  hasDefaultCertificateOfKey(Name keyName) throws PibImpl.Error
  {
    // Use a statement because it allows binding a blob for the query.
    SQLiteStatement statement = database_.compileStatement
      (SELECT_hasDefaultCertificateOfKey);
    try {
      statement.bindBlob(1, keyName.wireEncode().getImmutableArray());
      try {
        statement.simpleQueryForLong();
        return true;
      } catch (SQLiteDoneException ex) {
        // No match.
        return false;
      }
    } finally {
      statement.close();
    }
  }

  private SQLiteDatabase database_;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy