mirror of https://github.com/beemdevelopment/Aegis
Initial pass on adding support for persistence using sqlcipher
To keep the database schema simple we just save the otpauth:// url and let KeyInfo take care of checking for validity. This patch also includes multiple fixes for the KeyInfo class. We still need a separate activity to allow the user to enter their PIN/password. Currently, "test" is used as the password for the database.pull/41/head
parent
5994be2e4d
commit
8063ba11f1
@ -0,0 +1,16 @@
|
|||||||
|
package me.impy.aegis.crypto;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class CryptoUtils {
|
||||||
|
private CryptoUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void zero(char[] data) {
|
||||||
|
Arrays.fill(data, '\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void zero(byte[] data) {
|
||||||
|
Arrays.fill(data, (byte)0);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
package me.impy.aegis.db;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import net.sqlcipher.Cursor;
|
||||||
|
import net.sqlcipher.database.SQLiteDatabase;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import me.impy.aegis.KeyProfile;
|
||||||
|
import me.impy.aegis.crypto.KeyInfo;
|
||||||
|
|
||||||
|
public class Database {
|
||||||
|
private static Database instance;
|
||||||
|
private static Boolean libsLoaded = false;
|
||||||
|
private SQLiteDatabase db;
|
||||||
|
|
||||||
|
private Database(Context context, String filename, char[] password) {
|
||||||
|
DatabaseHelper helper = new DatabaseHelper(context, filename);
|
||||||
|
db = helper.getWritableDatabase(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Database createInstance(Context context, String filename, char[] password) {
|
||||||
|
// load the sqlcipher library, once
|
||||||
|
if (!libsLoaded) {
|
||||||
|
SQLiteDatabase.loadLibs(context);
|
||||||
|
libsLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new Database(context, filename, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// adds a key to the database and returns it's ID
|
||||||
|
public void addKey(KeyProfile profile) throws Exception {
|
||||||
|
db.execSQL("insert into otp (name, url) values (?, ?)",
|
||||||
|
new Object[]{ profile.Name, profile.Info.getURL() });
|
||||||
|
profile.ID = getLastID(db, "otp");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateKey(KeyProfile profile) throws Exception {
|
||||||
|
db.execSQL("update otp set name=? url=? where id=?",
|
||||||
|
new Object[]{ profile.Name, profile.Info.getURL(), profile.ID });
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeKey(KeyProfile profile) {
|
||||||
|
db.execSQL("delete from otp where id=?", new Object[]{ profile.ID });
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<KeyProfile> getKeys() throws Exception {
|
||||||
|
List<KeyProfile> list = new ArrayList<>();
|
||||||
|
Cursor cursor = db.rawQuery("select * from otp", null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
KeyProfile profile = new KeyProfile();
|
||||||
|
profile.ID = cursor.getInt(cursor.getColumnIndexOrThrow("id"));
|
||||||
|
profile.Name = cursor.getString(cursor.getColumnIndexOrThrow("name"));
|
||||||
|
|
||||||
|
String url = cursor.getString(cursor.getColumnIndexOrThrow("url"));
|
||||||
|
profile.Info = KeyInfo.FromURL(url);
|
||||||
|
|
||||||
|
list.add(profile);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getLastID(SQLiteDatabase db, String table) throws Exception {
|
||||||
|
Cursor cursor = db.rawQuery(String.format("select id from %s order by id desc limit 1", table), null);
|
||||||
|
try {
|
||||||
|
if (!cursor.moveToFirst()) {
|
||||||
|
throw new Exception("no items in the table, this should not happen here");
|
||||||
|
}
|
||||||
|
|
||||||
|
return cursor.getInt(cursor.getColumnIndexOrThrow("id"));
|
||||||
|
} finally {
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package me.impy.aegis.db;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import net.sqlcipher.database.SQLiteDatabase;
|
||||||
|
import net.sqlcipher.database.SQLiteOpenHelper;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import me.impy.aegis.crypto.KeyInfo;
|
||||||
|
import me.impy.aegis.encoding.Base32;
|
||||||
|
|
||||||
|
public class DatabaseHelper extends SQLiteOpenHelper {
|
||||||
|
// NOTE: increment this every time the schema is changed
|
||||||
|
public static final int Version = 1;
|
||||||
|
|
||||||
|
private static final String queryCreateOTPTable =
|
||||||
|
"create table otp (" +
|
||||||
|
"id integer primary key autoincrement, " +
|
||||||
|
"name varchar not null, " +
|
||||||
|
"url varchar not null)";
|
||||||
|
|
||||||
|
public DatabaseHelper(Context context, String filename) {
|
||||||
|
super(context, filename, null, Version);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCreate(SQLiteDatabase db) {
|
||||||
|
db.execSQL(queryCreateOTPTable);
|
||||||
|
}
|
||||||
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
|
//db.execSQL(SQL_DELETE_ENTRIES);
|
||||||
|
//onCreate(db);
|
||||||
|
}
|
||||||
|
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
|
onUpgrade(db, oldVersion, newVersion);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue