public BlobStore(string path, SymmetricKey encryptionKey) { if (path == null) { Log.To.Database.E(TAG, "path cannot be null in ctor, throwing..."); throw new ArgumentNullException("path"); } _path = path; EncryptionKey = encryptionKey; if (Directory.Exists(path)) { // Existing blob-store. VerifyExistingStore(); } else { // New blob store; create directory: Directory.CreateDirectory(path); if (!Directory.Exists(path)) { Log.To.Database.E(TAG, "Unable to create directory for: {0}", path); throw new InvalidOperationException(string.Format("Unable to create directory for: {0}", path)); } if (encryptionKey != null) { MarkEncrypted(true); } } }
public void TestUnencryptedDB() { // Create unencrypted DB: var options = new DatabaseOptions(); options.Create = true; var seekrit = manager.OpenDatabase("seekrit", options); Assert.IsNotNull(seekrit, "Failed to create db"); CreateDocumentWithProperties(seekrit, new Dictionary<string, object> { { "answer", 42 } }); Assert.DoesNotThrow(() => seekrit.Close().Wait(15000)); var key = new SymmetricKey(_wrong.KeyData); options.EncryptionKey = key; var e = Assert.Throws<CouchbaseLiteException>(() => seekrit = manager.OpenDatabase("seekrit", options), "Shouldn't have been able to reopen encrypted db with wrong password"); Assert.AreEqual(StatusCode.Unauthorized, e.Code); seekrit.ChangeEncryptionKey(null); options.EncryptionKey = null; seekrit = manager.OpenDatabase("seekrit", options); Assert.IsNotNull(seekrit, "Failed to reopen db"); Assert.AreEqual(1, seekrit.GetDocumentCount()); }
public void OneTimeSetUp() { base.SetUp(); Log.I(TAG, "Generating keys for test, this might take a while..."); _letmein = SymmetricKey.Create("letmein"); Log.I(TAG, "Keys completed (1/3)"); _letmeout = SymmetricKey.Create("letmeout"); Log.I(TAG, "Keys completed (2/3)"); _wrong = SymmetricKey.Create("wrong"); Log.I(TAG, "Keys completed (3/3)"); }
public void OneTimeSetUp() { var sw = System.Diagnostics.Stopwatch.StartNew(); Console.WriteLine("Generating keys for test, this might take a while..."); _letmein = SymmetricKey.Create("letmein"); Console.WriteLine("Keys completed (1/3)"); _letmeout = SymmetricKey.Create("letmeout"); Console.WriteLine("Keys completed (2/3)"); _wrong = SymmetricKey.Create("wrong"); Console.WriteLine("Keys completed (3/3)"); sw.Stop(); Console.WriteLine("Created three keys in {0}ms", sw.ElapsedMilliseconds); }
public AtomicAction ActionToChangeEncryptionKey(SymmetricKey newKey) { return(new AtomicAction(() => ForestDBBridge.Check(err => { var newc4key = default(C4EncryptionKey); if (newKey != null) { newc4key = new C4EncryptionKey(newKey.KeyData); } return Native.c4view_rekey(_indexDB, &newc4key, err); }), null, null)); }
public BlobStore(string path, SymmetricKey encryptionKey) { if (path == null) { throw new ArgumentNullException("path"); } _path = path; EncryptionKey = encryptionKey; FilePath directory = new FilePath(path); if (directory.Exists() && directory.IsDirectory()) { // Existing blob-store. VerifyExistingStore(); } else { // New blob store; create directory: directory.Mkdirs(); if (!directory.IsDirectory()) { throw new InvalidOperationException(string.Format("Unable to create directory for: {0}", directory)); } if (encryptionKey != null) { MarkEncrypted(true); } } }
private void ChangeEncryptionKey(SymmetricKey newKey) { if (!IsOpen) { Log.W(TAG, "ChangeEncryptionKey called on closed database"); return; } var action = Storage.ActionToChangeEncryptionKey(newKey); action.AddLogic(Attachments.ActionToChangeEncryptionKey(newKey)); action.AddLogic(() => Shared.SetValue("encryptionKey", "", Name, newKey), null, null); action.Run(); }
public bool Open(String path, bool readOnly, string schema, SymmetricKey encryptionKey) { if (IsOpen) return true; Path = path; _readOnly = readOnly; Factory = new TaskFactory(new SingleThreadScheduler()); try { int readFlag = readOnly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; int writer_flags = SQLITE_OPEN_FILEPROTECTION_COMPLETEUNLESSOPEN | readFlag | SQLITE_OPEN_FULLMUTEX; OpenSqliteConnection(writer_flags, encryptionKey, out _writeConnection); #if ENCRYPTION if (!Decrypt(encryptionKey, _writeConnection)) { throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.Unauthorized, TAG, "Decryption of database failed"); } #endif if(schema != null && GetVersion() == 0) { foreach (var statement in schema.Split(';')) { ExecSQL(statement); } } const int reader_flags = SQLITE_OPEN_FILEPROTECTION_COMPLETEUNLESSOPEN | SQLITE_OPEN_READONLY | SQLITE_OPEN_FULLMUTEX; OpenSqliteConnection(reader_flags, encryptionKey, out _readConnection); #if ENCRYPTION if(!Decrypt(encryptionKey, _readConnection)) { throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.Unauthorized, TAG, "Decryption of database failed"); } #endif } catch(CouchbaseLiteException) { Log.To.Database.W(TAG, "Error opening SQLite storage engine, rethrowing..."); throw; } catch (Exception ex) { throw Misc.CreateExceptionAndLog(Log.To.Database, ex, TAG, "Failed to open SQLite storage engine"); } return true; }
// Returns true on success, false if encryption key is wrong, throws exception for other cases public bool Decrypt(SymmetricKey encryptionKey, sqlite3 connection) { #if !ENCRYPTION Log.To.Database.E(TAG, "Encryption not supported on this store, throwing..."); throw new InvalidOperationException("Encryption not supported on this store"); #else if (encryptionKey != null) { // http://sqlcipher.net/sqlcipher-api/#key var sql = String.Format("PRAGMA key = \"x'{0}'\"", encryptionKey.HexData); try { ExecSQL(sql, connection); } catch(CouchbaseLiteException) { Log.To.Database.E(TAG, "Decryption operation failed, rethrowing..."); throw; } catch(Exception e) { throw Misc.CreateExceptionAndLog(Log.To.Database, e, TAG, "Decryption operation failed"); } } // Verify that encryption key is correct (or db is unencrypted, if no key given): var result = raw.sqlite3_exec(connection, "SELECT count(*) FROM sqlite_master"); if (result != raw.SQLITE_OK) { if (result == raw.SQLITE_NOTADB) { return false; } else { throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.DbError, TAG, "Cannot read from database ({0})", result); } } return true; #endif }
public bool MockDecrypt(SymmetricKey encryptionKey) { var givenKeyData = encryptionKey != null ? encryptionKey.KeyData : new byte[0]; var keyPath = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(Path), "mock_key"); if (!File.Exists(keyPath)) { File.WriteAllBytes(keyPath, givenKeyData); } else { var actualKeyData = File.ReadAllBytes(keyPath); if (!givenKeyData.SequenceEqual(actualKeyData)) { return false; } } return true; }
public AtomicAction ActionToChangeEncryptionKey(SymmetricKey newKey) { var retVal = new AtomicAction(() => ForestDBBridge.Check(err => { var newc4key = default(C4EncryptionKey); if (newKey != null) { newc4key = new C4EncryptionKey(newKey.KeyData); } return Native.c4db_rekey(Forest, &newc4key, err); }), null, null); foreach (var viewName in GetAllViews()) { var store = GetViewStorage(viewName, false) as ForestDBViewStore; if (store == null) { continue; } retVal.AddLogic(store.ActionToChangeEncryptionKey(newKey)); } return retVal; }
public void SetEncryptionKey(SymmetricKey key) { _encryptionKey = key; }
public void ChangeEncryptionKey(SymmetricKey newKey) { ActionToChangeEncryptionKey(newKey).Run(); }
public AtomicAction ActionToChangeEncryptionKey(SymmetricKey newKey) { var action = new AtomicAction(); // Find all the blob files: var blobs = default(string[]); var oldKey = EncryptionKey; blobs = Directory.GetFiles(_path, "*" + FileExtension); if (blobs.Length == 0) { // No blobs, so nothing to encrypt. Just add/remove the encryption marker file: action.AddLogic(() => { Log.To.NoDomain.D(TAG, "{0} {1}", (newKey != null) ? "encrypting" : "decrypting", _path); Log.To.NoDomain.D(TAG, " No blobs to copy; done."); EncryptionKey = newKey; MarkEncrypted(newKey != null); }, () => { EncryptionKey = oldKey; MarkEncrypted(oldKey != null); }, null); return action; } // Create a new directory for the new blob store. Have to do this now, before starting the // action, because farther down we create an action to move it... var tempPath = Path.Combine(Path.GetTempPath(), String.Format("CouchbaseLite-Temp-{0}", Misc.CreateGUID())); action.AddLogic(() => { Log.To.NoDomain.D(TAG, "{0} {1}", (newKey != null) ? "encrypting" : "decrypting", _path); Directory.CreateDirectory(tempPath); }, () => Directory.Delete(tempPath, true), null); var tempStore = default(BlobStore); action.AddLogic(() => { tempStore = new BlobStore(tempPath, newKey); tempStore.MarkEncrypted(true); }, null, null); // Copy each of my blobs into the new store (which will update its encryption): action.AddLogic(() => { foreach(var blobName in blobs) { // Copy file by reading with old key and writing with new one: Log.To.NoDomain.D(TAG, " Copying {0}", blobName); Stream readStream = File.Open(blobName, FileMode.Open, FileAccess.Read, FileShare.Read); if(EncryptionKey != null) { readStream = EncryptionKey.DecryptStream(readStream); } var writer = new BlobStoreWriter(tempStore); try { writer.Read(readStream); writer.Finish(); writer.Install(); } catch(Exception) { writer.Cancel(); throw; } finally { readStream.Dispose(); } } }, null, null); // Replace the attachment dir with the new one: action.AddLogic(AtomicAction.MoveDirectory(tempPath, _path)); // Finally update EncryptionKey: action.AddLogic(() => { EncryptionKey = newKey; }, () => { EncryptionKey = oldKey; }, null); return action; }
private IEnumerable<byte> Decrypt(byte[] key, byte[] data) { var symmetricKey = new SymmetricKey(DecryptRSA(GetPrivateKey(), key)); if(symmetricKey == null) { return null; } return symmetricKey.DecryptData(data); }
private byte[][] Encrypt(byte[] data) { try { var symmetricKey = new SymmetricKey(); var encryptedKey = EncryptRSA(GetPublicKey(), symmetricKey.KeyData); if(encryptedKey == null) { return null; } var encryptedData = new byte[2][]; encryptedData[0] = encryptedKey; encryptedData[1] = symmetricKey.EncryptData(data); return encryptedData; } catch(Exception e) { Log.To.NoDomain.E(Tag, "Error during encryption", e); return null; } }
/// <summary> /// Change the encryption key used to secure this database /// </summary> /// <param name="newPassword">The new password to derive the key from</param> public void ChangeEncryptionPassword(string newPassword) { var newKey = new SymmetricKey(newPassword); ChangeEncryptionKey(newKey); }
/// <summary> /// Change the encryption key used to secure this database /// </summary> /// <param name="newPassKey">The new password to derive the key from</param> public void ChangeEncryptionKey(IEnumerable<byte> newPassKey) { var newKey = new SymmetricKey(newPassKey.ToArray()); ChangeEncryptionKey(newKey); }
private void VerifyExistingStore() { var markerPath = Path.Combine(_path, ENCRYPTION_MARKER_FILENAME); var fileExists = File.Exists(markerPath); var encryptionAlg = default(string); try { encryptionAlg = fileExists ? File.ReadAllText(markerPath) : null; } catch(Exception e) { throw Misc.CreateExceptionAndLog(Log.To.Database, e, TAG, "Error verifying blob store"); } if (encryptionAlg != null) { // "_encryption" file is present, so make sure we support its format & have a key: if (EncryptionKey == null) { throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.Unauthorized, TAG, "Opening encrypted blob-store without providing a key"); } else if (ENCRYPTION_ALGORITHM != encryptionAlg) { throw Misc.CreateExceptionAndLog(Log.To.Database, StatusCode.Unauthorized, TAG, "Blob-store uses unrecognized encryption '{0}'", encryptionAlg); } } else if (!fileExists) { // No "_encryption" file was found, so on-disk store isn't encrypted: var encryptionKey = EncryptionKey; if (encryptionKey != null) { // This store was created before the db encryption fix, so its files are not // encrypted, even though they should be. Remedy that: Log.To.NoDomain.I(TAG, "**** BlobStore should be encrypted; fixing it now..."); EncryptionKey = null; ChangeEncryptionKey(encryptionKey); } } }
void OpenSqliteConnection(int flags, SymmetricKey encryptionKey, out sqlite3 db) { LastErrorCode = raw.sqlite3_open_v2(Path, out db, flags, null); if (LastErrorCode != raw.SQLITE_OK) { Path = null; var errMessage = "Failed to open SQLite storage engine at path {0}".Fmt(Path); throw new CouchbaseLiteException(errMessage, StatusCode.DbError); } #if !__ANDROID__ && !NET_3_5 && VERBOSE var i = 0; var val = raw.sqlite3_compileoption_get(i); while (val != null) { Log.V(TAG, "Sqlite Config: {0}".Fmt(val)); val = raw.sqlite3_compileoption_get(++i); } #endif Log.D(TAG, "Open {0} (flags={1}{2})", Path, flags, (encryptionKey != null ? ", encryption key given" : "")); raw.sqlite3_create_collation(db, "JSON", null, CouchbaseSqliteJsonUnicodeCollationFunction.Compare); raw.sqlite3_create_collation(db, "JSON_ASCII", null, CouchbaseSqliteJsonAsciiCollationFunction.Compare); raw.sqlite3_create_collation(db, "JSON_RAW", null, CouchbaseSqliteJsonRawCollationFunction.Compare); raw.sqlite3_create_collation(db, "REVID", null, CouchbaseSqliteRevIdCollationFunction.Compare); }
public bool Open(String path, SymmetricKey encryptionKey = null) { if (IsOpen) return true; Path = path; Factory = new TaskFactory(new SingleThreadScheduler()); try { shouldCommit = false; const int writer_flags = SQLITE_OPEN_FILEPROTECTION_COMPLETEUNLESSOPEN | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX; OpenSqliteConnection(writer_flags, encryptionKey, out _writeConnection); const int reader_flags = SQLITE_OPEN_FILEPROTECTION_COMPLETEUNLESSOPEN | SQLITE_OPEN_READONLY | SQLITE_OPEN_FULLMUTEX; OpenSqliteConnection(reader_flags, encryptionKey, out _readConnection); if (!Decrypt(encryptionKey)) { throw new CouchbaseLiteException(StatusCode.Unauthorized); } } catch(CouchbaseLiteException) { Log.W(TAG, "Error opening SQLite storage engine"); throw; } catch (Exception ex) { throw new CouchbaseLiteException("Failed to open SQLite storage engine", ex) { Code = StatusCode.Exception }; } return true; }
/// <summary> /// Change the encryption key used to secure this database /// </summary> /// <param name="newKey">The new key to use</param> public void ChangeEncryptionKey(SymmetricKey newKey) { if(!IsOpen) { Log.To.Database.W(TAG, "{0} ChangeEncryptionKey called on closed database, returning early...", this); return; } var action = Storage.ActionToChangeEncryptionKey(newKey); action.AddLogic(Attachments.ActionToChangeEncryptionKey(newKey)); action.AddLogic(() => Shared.SetValue("encryptionKey", "", Name, newKey), null, null); action.Run(); }
// Returns true on success, false if encryption key is wrong, throws exception for other cases public bool Decrypt(SymmetricKey encryptionKey) { var hasRealEncryption = false; try { hasRealEncryption = raw.sqlite3_compileoption_used("SQLITE_HAS_CODEC") != 0; } catch(EntryPointNotFoundException) { // Android omits the compileoption_used from its system SQLite hasRealEncryption = false; } if (encryptionKey != null) { if (!hasRealEncryption) { throw new CouchbaseLiteException("Encryption not available (app not built with SQLCipher)", StatusCode.NotImplemented); } // http://sqlcipher.net/sqlcipher-api/#key var sql = String.Format("PRAGMA key = \"x'{0}'\"", encryptionKey.HexData); try { ExecSQL(sql, _writeConnection); ExecSQL(sql, _readConnection); } catch(CouchbaseLiteException) { Log.W(TAG, "Decryption operation failed"); throw; } catch(Exception e) { throw new CouchbaseLiteException("Decryption operation failed", e); } } // Verify that encryption key is correct (or db is unencrypted, if no key given): var result = raw.sqlite3_exec(_readConnection, "SELECT count(*) FROM sqlite_master"); if (result != raw.SQLITE_OK) { if (result == raw.SQLITE_NOTADB) { return false; } else { throw new CouchbaseLiteException(String.Format("Cannot read from database ({0})", result), StatusCode.DbError); } } return true; }
public bool Open(String path, bool readOnly, string schema, SymmetricKey encryptionKey) { if (IsOpen) return true; Path = path; _readOnly = readOnly; Factory = new TaskFactory(new SingleThreadScheduler()); try { shouldCommit = false; int readFlag = readOnly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; int writer_flags = SQLITE_OPEN_FILEPROTECTION_COMPLETEUNLESSOPEN | readFlag | SQLITE_OPEN_FULLMUTEX; OpenSqliteConnection(writer_flags, encryptionKey, out _writeConnection); if (!Decrypt(encryptionKey, _writeConnection)) { throw new CouchbaseLiteException(StatusCode.Unauthorized); } if(schema != null && GetVersion() == 0) { foreach (var statement in schema.Split(';')) { ExecSQL(statement); } } const int reader_flags = SQLITE_OPEN_FILEPROTECTION_COMPLETEUNLESSOPEN | SQLITE_OPEN_READONLY | SQLITE_OPEN_FULLMUTEX; OpenSqliteConnection(reader_flags, encryptionKey, out _readConnection); if(!Decrypt(encryptionKey, _readConnection)) { throw new CouchbaseLiteException(StatusCode.Unauthorized); } } catch(CouchbaseLiteException) { Log.W(TAG, "Error opening SQLite storage engine"); throw; } catch (Exception ex) { throw new CouchbaseLiteException("Failed to open SQLite storage engine", ex) { Code = StatusCode.Exception }; } return true; }