Searchable Encryption applied to Firebase database

Firebase is a powerful and popular mobile backend offered “as a service” that you can start with it in minutes; it offers data storage, user authentication mechanisms and many other features for your mobile app. Data is stored as JSON and synchronized in realtime to every connected client and it’s encrypted on the wire (using HTTPS), but it remains unencrypted at the server/cloud side which makes Firebase not suitable for apps that need to store sensitive information like apps in mhealth or fintech.

Using IQrypt, you can build apps that store sensitive information like patient’s data and, at the same time, keeping the benefits of the simplicity and scalability of Firebase without sacrifice any Firebase features like queries/search even the data is encrypted.

Let’s take a look how easily you can integrate IQrypt within Firebase Android client; first let’s setup the Firebase app which is 1 minute task: just login on your Firebase account and put the name of your new app and press ‘CREATE NEW APP’:

firebaseNewApp

Your database is ready now, you can access it via the URL provided (eq: https://iqrypt.firebaseio.com/). On production, you will need to add authentication mechanisms, but for our demo, we’ll let it publicly accessible.

Now let’s go to the client and let’s start with our data model, first Patient entity:

public class Patient
{
    // Patient object contains following properties
    public String ObjectId ;
    public String SSN ;
    public String PhotoThumbnail ;
    public String PhotoDetail ;
    public String FirstName ;
    public String LastName ;
    public String Gender ;
    public String Location ;
    public String Building ;
    public int Floor ;
    public Date VisitDate;
    public String Treatment ;
    public String Status ;
    public String Doctor ;
    public PatientDetails PatientDetailsData;
    public String Version ;
    public boolean IsVisited;

}

PatientDetails contains other additional data (the definition can be found on Github repository).
The goal is that an entire Patient object is encrypted with RND encryption scheme and we will extract 2 tags that we want to search by:

  • SSN (Social Security Number) will be encrypted with DET so we can make equality queries
  • VisitDate will be encrypted with OPE so we can make range queries

Let’s create a Repository class that will manage our data and initialize it:

 

public class FirebaseRepository {

    Firebase patients;
    IEncryptor encryptorRND;
    IEncryptor encryptorDET ;
    IEncryptor encryptorOPE ;
    public FirebaseRepository()
    {

        Firebase myFirebaseRef = new Firebase("https://iqrypt.firebaseio.com/");
        patients=myFirebaseRef.child("patients");
        //init IQrypt
        IQryptConfigurator.setEncryptionChiper(Cipher.AES256, "my_super_secret");
        encryptorRND = EncryptorFactory.getEncryptor(EncryptionType.RND);
        encryptorDET = EncryptorFactory.getEncryptor(EncryptionType.DET);
        encryptorOPE = EncryptorFactory.getEncryptor(EncryptionType.OPE);
    }
....
}

Above, we got a reference to Patients JSON root node and we have also set IQrypt cipher + password(encryption key material) and create encryptors instances for the encryption schemes we need.

Now let’s create the method that will insert data into the database:

 public String createPatient(Patient patient) {

        Firebase newPatientRef = patients.push();


        patient.ObjectId = newPatientRef.getKey();
        Map<String, String> docContent = new HashMap<String, String>();

        docContent.put("patient", encryptorRND.encrypt(patient));
        //tags to search by
        docContent.put("ssn", encryptorDET.encrypt(patient.SSN));
        docContent.put("visitdate", encryptorOPE.encrypt(patient.VisitDate));

        newPatientRef.setValue(docContent);

        Log.d("LOG_PATIENT", "Patient with id:" + patient.ObjectId + " created");
        return newPatientRef.getKey();
    }

So when we call

Firebase newPatientRef = patients.push();

Firebase will generate a Key(which ca be used further to uniquely identify a patient), then we encrypt entire Patient object with RND encryption scheme, we’ll extract two tags that we will want to search by, and we’ll encrypt them with DET and respectively with OPE.

If you will call this method by passing a full Patient object, you will see on Firebase data browser something like this:

firebaseServer

We will implement now the methods that execute queries, first getBySSN:

public void  getPatientBySSN(String SSN) {
        String SSNEncrypted = encryptorDET.encrypt(SSN);
        Query queryRef = patients.orderByChild("ssn").equalTo(SSNEncrypted);


        queryRef.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot snapshot, String previousChild) {

                String patientEncrypted=snapshot.child("patient").getValue(String.class);
                Patient pDec= (Patient) encryptorRND.decrypt(patientEncrypted,Patient.class);
                Log.d("LOG_PATIENT_BY_SSN", "Patient with SSN:" + pDec.SSN +" loaded" );
            }
.....
          });

}

We encrypt first the SSN and then send its encrypted value to Firebase engine which executes the query without decrypting the SSN.

Let’s see now how we can execute a range query using OPE scheme:

public void getPatientsVisitedBetween(Date start, Date end) {
        String encryptedStart=encryptorOPE.encrypt(start);
        String encryptedEnd=encryptorOPE.encrypt(end);

        Query queryRef = patients.orderByChild("visitdate").startAt(encryptedStart).endAt(encryptedEnd);

        queryRef.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot snapshot, String previousChild) {

                String patientEncrypted=snapshot.child("patient").getValue(String.class);
                Patient pDec= (Patient) encryptorRND.decrypt(patientEncrypted,Patient.class);
                Log.d("LOG_PATIENT_BTW_DATES", "Patient with VisitDate:" + pDec.VisitDate +" loaded" );

            }
          
           ........
           });
}

So we have encrypted first the ‘start’ and ‘end’ values and then pass that values to Firebase query engine. The Firebase service will respond in the callback by loading only the Patient objects which has the VisitDate between specified dates (without decrypting anything).

Full source code can be found on Github.

Encrypt and stay safe!