Merge pull request #282 from realm/al-encryption

Add encryption support
This commit is contained in:
Ari Lazier 2016-02-26 11:55:03 -08:00
commit 66404c4d81
5 changed files with 111 additions and 37 deletions

View File

@ -31,6 +31,7 @@
#include <cassert> #include <cassert>
using namespace realm; using namespace realm;
using RJSAccessor = realm::NativeAccessor<JSValueRef, JSContextRef>;
class RJSRealmDelegate : public BindingContext { class RJSRealmDelegate : public BindingContext {
public: public:
@ -152,48 +153,52 @@ JSObjectRef RealmConstructor(JSContextRef ctx, JSObjectRef constructor, size_t a
Realm::Config config; Realm::Config config;
std::map<std::string, realm::ObjectDefaults> defaults; std::map<std::string, realm::ObjectDefaults> defaults;
std::map<std::string, JSObjectRef> constructors; std::map<std::string, JSObjectRef> constructors;
switch (argumentCount) { if (argumentCount == 0) {
case 0: config.path = RJSDefaultPath();
config.path = RJSDefaultPath(); }
break; else if (argumentCount == 1) {
case 1: { JSValueRef value = arguments[0];
JSValueRef value = arguments[0]; if (JSValueIsString(ctx, value)) {
if (JSValueIsString(ctx, value)) { config.path = RJSValidatedStringForValue(ctx, value, "path");
config.path = RJSValidatedStringForValue(ctx, value, "path"); }
break; else if (JSValueIsObject(ctx, value)) {
JSObjectRef object = RJSValidatedValueToObject(ctx, value);
static JSStringRef pathString = JSStringCreateWithUTF8CString("path");
JSValueRef pathValue = RJSValidatedPropertyValue(ctx, object, pathString);
if (!JSValueIsUndefined(ctx, pathValue)) {
config.path = RJSValidatedStringForValue(ctx, pathValue, "path");
}
else {
config.path = RJSDefaultPath();
} }
if (JSValueIsObject(ctx, value)) {
JSObjectRef object = RJSValidatedValueToObject(ctx, value);
static JSStringRef pathString = JSStringCreateWithUTF8CString("path"); static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema");
JSValueRef pathValue = RJSValidatedPropertyValue(ctx, object, pathString); JSValueRef schemaValue = RJSValidatedPropertyValue(ctx, object, schemaString);
if (!JSValueIsUndefined(ctx, pathValue)) { if (!JSValueIsUndefined(ctx, schemaValue)) {
config.path = RJSValidatedStringForValue(ctx, pathValue, "path"); config.schema.reset(new Schema(RJSParseSchema(ctx, RJSValidatedValueToObject(ctx, schemaValue), defaults, constructors)));
} }
else {
config.path = RJSDefaultPath();
}
static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema"); static JSStringRef schemaVersionString = JSStringCreateWithUTF8CString("schemaVersion");
JSValueRef schemaValue = RJSValidatedPropertyValue(ctx, object, schemaString); JSValueRef versionValue = RJSValidatedPropertyValue(ctx, object, schemaVersionString);
if (!JSValueIsUndefined(ctx, schemaValue)) { if (JSValueIsNumber(ctx, versionValue)) {
config.schema.reset(new Schema(RJSParseSchema(ctx, RJSValidatedValueToObject(ctx, schemaValue), defaults, constructors))); config.schema_version = RJSValidatedValueToNumber(ctx, versionValue);
} }
else {
config.schema_version = 0;
}
static JSStringRef schemaVersionString = JSStringCreateWithUTF8CString("schemaVersion"); static JSStringRef encryptionKeyString = JSStringCreateWithUTF8CString("encryptionKey");
JSValueRef versionValue = RJSValidatedPropertyValue(ctx, object, schemaVersionString); JSValueRef encryptionKeyValue = RJSValidatedPropertyValue(ctx, object, encryptionKeyString);
if (JSValueIsNumber(ctx, versionValue)) { if (!JSValueIsUndefined(ctx, encryptionKeyValue)) {
config.schema_version = RJSValidatedValueToNumber(ctx, versionValue); std::string encryptionKey = RJSAccessor::to_binary(ctx, encryptionKeyValue);
} config.encryption_key = std::vector<char>(encryptionKey.begin(), encryptionKey.end());;
else {
config.schema_version = 0;
}
break;
} }
} }
default: }
*jsException = RJSMakeError(ctx, "Invalid arguments when constructing 'Realm'"); else {
return NULL; *jsException = RJSMakeError(ctx, "Invalid arguments when constructing 'Realm'");
return NULL;
} }
ensure_directory_exists_for_file(config.path); ensure_directory_exists_for_file(config.path);
SharedRealm realm = Realm::get_shared_realm(config); SharedRealm realm = Realm::get_shared_realm(config);

View File

@ -72,6 +72,9 @@ Realm::Realm(Config config)
m_group = m_read_only_group.get(); m_group = m_read_only_group.get();
} }
else { else {
if (m_config.encryption_key.data() && m_config.encryption_key.size() != 64) {
throw InvalidEncryptionKeyException();
}
m_history = realm::make_client_history(m_config.path, m_config.encryption_key.data()); m_history = realm::make_client_history(m_config.path, m_config.encryption_key.data());
SharedGroup::DurabilityLevel durability = m_config.in_memory ? SharedGroup::durability_MemOnly : SharedGroup::DurabilityLevel durability = m_config.in_memory ? SharedGroup::durability_MemOnly :
SharedGroup::durability_Full; SharedGroup::durability_Full;

View File

@ -199,6 +199,11 @@ namespace realm {
public: public:
UnitializedRealmException(std::string message) : std::runtime_error(message) {} UnitializedRealmException(std::string message) : std::runtime_error(message) {}
}; };
class InvalidEncryptionKeyException : public std::runtime_error {
public:
InvalidEncryptionKeyException() : std::runtime_error("Encryption key must be 64 bytes.") {}
};
} }
#endif /* defined(REALM_REALM_HPP) */ #endif /* defined(REALM_REALM_HPP) */

View File

@ -0,0 +1,60 @@
////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////
'use strict';
var Realm = require('realm');
var BaseTest = require('./base-test');
var TestCase = require('./asserts');
var Schemas = require('./schemas');
module.exports = BaseTest.extend({
testEncryptedInvalidKeys: function() {
// test failure with invalid keys
TestCase.assertThrows(function() {
new Realm({schema: [Schemas.TestObject], encryptionKey: " ".repeat(64)});
}, "Encryption Key must be an ArrayBuffer");
TestCase.assertThrows(function() {
new Realm({schema: [Schemas.TestObject], encryptionKey: new Int8Array(63)});
}, "Encryption Key must be 64 byes");
},
testEncryptionValidKey: function() {
var key = new Int8Array(64);
key[0] = 1;
var realm = new Realm({schema: [Schemas.TestObject], encryptionKey: key});
realm.write(function() {
realm.create('TestObject', {doubleCol: 1});
TestCase.assertEqual(realm.objects('TestObject').length, 1);
});
// test failure with different or missing
realm.close();
TestCase.assertThrows(function() {
new Realm({schema: [Schemas.TestObject], encryptionKey: new Int8Array(64)});
});
TestCase.assertThrows(function() {
new Realm({schema: [Schemas.TestObject]});
});
// test can reopen with original key
var realm = new Realm({schema: [Schemas.TestObject], encryptionKey: key});
TestCase.assertEqual(realm.objects('TestObject').length, 1);
},
});

View File

@ -24,6 +24,7 @@ var TESTS = {
RealmTests: require('./realm-tests'), RealmTests: require('./realm-tests'),
ResultsTests: require('./results-tests'), ResultsTests: require('./results-tests'),
QueryTests: require('./query-tests'), QueryTests: require('./query-tests'),
EncryptionTests: require('./encryption-tests'),
}; };
var SPECIAL_METHODS = { var SPECIAL_METHODS = {